diff --git a/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml b/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml index 1e7af47cfc9..45712fcdf9b 100644 --- a/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml +++ b/tests/common/plugins/conditional_mark/tests_mark_conditions.yaml @@ -3969,12 +3969,12 @@ override_config_table/test_override_config_table.py::test_load_minigraph_with_go ######################################## packet_trimming: skip: - reason: "Packet trimming is not supported on 202505. Packet trimming cases require PR https://github.com/sonic-net/sonic-buildimage/pull/22869, but KVM does not support it, so skip trimming case on KVM. Packet-trimming tests are only supported on certain Mellanox SN5640 and Arista 7060X6 SKUs." + reason: "Packet trimming is not supported on 202505. Packet trimming cases require PR https://github.com/sonic-net/sonic-buildimage/pull/22869, but KVM does not support it, so skip trimming case on KVM. Packet-trimming tests are only supported on certain Mellanox SN5640 and Arista 7060X6 SKUs. On sonic-vpp (asic_type 'vpp') the symmetric core trimming cases are enabled; deferred cases are skipped per-test (tracking sonic-net/sonic-buildimage#25789)." conditions_logical_operator: or conditions: - "release in ['202505']" - "asic_type in ['vs']" - - "hwsku not in ['Arista-7060X6-64PE-B-C448O16', 'Arista-7060X6-64PE-B-C512S2', 'Mellanox-SN5640-C448O16', 'Mellanox-SN5640-C512S2']" + - "hwsku not in ['Arista-7060X6-64PE-B-C448O16', 'Arista-7060X6-64PE-B-C512S2', 'Mellanox-SN5640-C448O16', 'Mellanox-SN5640-C512S2'] and asic_type != 'vpp'" xfail: reason: "Test case has issue on the t0-isolated-d256u256s2 topo." conditions: @@ -3994,16 +3994,80 @@ packet_trimming/test_packet_trimming_asymmetric.py::TestPacketTrimmingAsymmetric packet_trimming/test_packet_trimming_symmetric.py::TestPacketTrimmingSymmetric::test_trimming_counters_with_feature_toggle: skip: - reason: "TH5 clears trim counters when packet trimming is turned off, skipping until trim counter persistance is supported." + reason: "TH5 clears trim counters when packet trimming is turned off, skipping until trim counter persistance is supported. Trim counters are also deferred on sonic-vpp (tracking sonic-net/sonic-buildimage#25789)." + conditions_logical_operator: or conditions: - "asic_gen in ['th5']" + - "asic_type in ['vpp']" packet_trimming/test_packet_trimming_symmetric.py::TestPacketTrimmingSymmetric::test_trimming_with_srv6: + skip: + reason: "SRv6 with packet trimming is deferred on sonic-vpp; the SRv6 trim path is not yet implemented in the VPP dataplane (tracking sonic-net/sonic-buildimage#25789)." + conditions: + - "asic_type in ['vpp']" xfail: reason: "vlan decap is disabled in 202412 image" conditions: - "'isolated' in topo_name and release in ['202412']" +################################################# +## sonic-vpp: deferred packet_trimming cases ## +## (symmetric core cases run; see HLD §13/§14) ## +################################################# +packet_trimming/test_packet_trimming_asymmetric.py: + skip: + reason: "Asymmetric (per-port DSCP) packet trimming is deferred on sonic-vpp; only symmetric core trimming is enabled (tracking sonic-net/sonic-buildimage#25789)." + conditions: + - "asic_type in ['vpp']" + +packet_trimming/test_packet_trimming_config_asymmetric.py: + skip: + reason: "Asymmetric (FROM_TC) packet-trimming GCU config validation is deferred on sonic-vpp; only symmetric core trimming is enabled (tracking sonic-net/sonic-buildimage#25789)." + conditions: + - "asic_type in ['vpp']" + +packet_trimming/test_packet_trimming_config_symmetric.py: + skip: + reason: "Symmetric packet-trimming GCU config validation is deferred on sonic-vpp; out of scope for the initial dataplane enablement (tracking sonic-net/sonic-buildimage#25789)." + conditions: + - "asic_type in ['vpp']" + +packet_trimming/test_packet_trimming_symmetric.py::TestPacketTrimmingSymmetric::test_acl_action_with_trimming: + skip: + reason: "ACL DISABLE_TRIM action with packet trimming is deferred on sonic-vpp; not yet implemented in the VPP dataplane (tracking sonic-net/sonic-buildimage#25789)." + conditions: + - "asic_type in ['vpp']" + +packet_trimming/test_packet_trimming_symmetric.py::TestPacketTrimmingSymmetric::test_stability_during_feature_toggles: + skip: + reason: "Feature-toggle stability with packet trimming is deferred on sonic-vpp; not yet validated (tracking sonic-net/sonic-buildimage#25789)." + conditions: + - "asic_type in ['vpp']" + +packet_trimming/test_packet_trimming_symmetric.py::TestPacketTrimmingSymmetric::test_trimming_during_port_admin_toggle: + skip: + reason: "Port admin-toggle with packet trimming is deferred on sonic-vpp; not yet validated (tracking sonic-net/sonic-buildimage#25789)." + conditions: + - "asic_type in ['vpp']" + +packet_trimming/test_packet_trimming_symmetric.py::TestPacketTrimmingSymmetric::test_trimming_with_reload_and_reboot: + skip: + reason: "Reload/reboot persistence of packet trimming is deferred on sonic-vpp; config persistence not yet validated (tracking sonic-net/sonic-buildimage#25789)." + conditions: + - "asic_type in ['vpp']" + +packet_trimming/test_packet_trimming_symmetric.py::TestPacketTrimmingSymmetric::test_trimming_counters: + skip: + reason: "Trim counters are deferred on sonic-vpp; trim drop/sent counters are not yet implemented (tracking sonic-net/sonic-buildimage#25789)." + conditions: + - "asic_type in ['vpp']" + +packet_trimming/test_packet_trimming_symmetric.py::TestPacketTrimmingSymmetric::test_port_mirror_with_trimming: + skip: + reason: "Port mirroring with packet trimming is deferred on sonic-vpp; the mirror trim path is not yet implemented (tracking sonic-net/sonic-buildimage#25789)." + conditions: + - "asic_type in ['vpp']" + ####################################### ##### pc ##### ####################################### diff --git a/tests/packet_trimming/conftest.py b/tests/packet_trimming/conftest.py index 4e7e796acd4..fc470248166 100644 --- a/tests/packet_trimming/conftest.py +++ b/tests/packet_trimming/conftest.py @@ -16,6 +16,8 @@ configure_tc_to_dscp_map, set_buffer_profile_for_block_queue, set_buffer_profile_for_trim_queue, create_blocking_scheduler, configure_trimming_action, cleanup_trimming_acl, get_queue_id_by_dscp, get_test_ports, create_trim_queue_test_buffer_profile, delete_trim_queue_test_buffer_profile, + delete_buffer_queue_for_trim_queue, + delete_buffer_queue_for_block_queue, delete_created_block_queue_buffer_profiles, is_queue_level_trim_sent_drop_supported) @@ -184,6 +186,12 @@ def setup_trimming(duthost, test_params, trim_counter_params, request): logger.info("Prepare packet trimming related configurations") platform = duthost.facts['platform'] + # Track the blocking-queue buffer config this fixture adds so teardown can + # remove exactly what it created. On platforms whose base config already + # defines these profiles nothing is created and nothing is torn down. + created_block_profiles = set() + block_queue_bindings = [] + with allure.step("Backup configuration"): logger.info("Backup configuration before trimming test") duthost.shell("sudo config save -y /etc/sonic/config_db_before_trimming_test.json") @@ -209,10 +217,14 @@ def setup_trimming(duthost, test_params, trim_counter_params, request): counter_uplink_port = trim_counter_params['egress_ports'][0] counter_block_interface = counter_uplink_port['dut_members'] logger.info(f"Apply uplink buffer profile to interfaces: {block_interface}") - set_buffer_profile_for_block_queue(duthost, block_interface, test_params['block_queue'], - test_params['trim_buffer_profiles']['uplink']) - set_buffer_profile_for_block_queue(duthost, counter_block_interface, trim_counter_params['block_queue'], - trim_counter_params['trim_buffer_profiles']['uplink']) + created_block_profiles.add( + set_buffer_profile_for_block_queue(duthost, block_interface, test_params['block_queue'], + test_params['trim_buffer_profiles']['uplink'])) + block_queue_bindings.append((block_interface, test_params['block_queue'])) + created_block_profiles.add( + set_buffer_profile_for_block_queue(duthost, counter_block_interface, trim_counter_params['block_queue'], + trim_counter_params['trim_buffer_profiles']['uplink'])) + block_queue_bindings.append((counter_block_interface, trim_counter_params['block_queue'])) create_trim_queue_test_buffer_profile(duthost) set_buffer_profile_for_trim_queue(duthost, block_interface) @@ -221,8 +233,10 @@ def setup_trimming(duthost, test_params, trim_counter_params, request): downlink_port = test_params['egress_ports'][1] block_interface = downlink_port['dut_members'] logger.info(f"Apply downlink buffer profile to {block_interface}:{test_params['block_queue']}") - set_buffer_profile_for_block_queue(duthost, block_interface, test_params['block_queue'], - test_params['trim_buffer_profiles']['downlink']) + created_block_profiles.add( + set_buffer_profile_for_block_queue(duthost, block_interface, test_params['block_queue'], + test_params['trim_buffer_profiles']['downlink'])) + block_queue_bindings.append((block_interface, test_params['block_queue'])) set_buffer_profile_for_trim_queue(duthost, block_interface) # Also set the downlink buffer profile for the block queue used in counter tests, if applicable. @@ -231,8 +245,10 @@ def setup_trimming(duthost, test_params, trim_counter_params, request): counter_block_interface = counter_downlink_port['dut_members'] logger.info(f"Apply downlink buffer profile to " f"{counter_block_interface}:{trim_counter_params['block_queue']}") - set_buffer_profile_for_block_queue(duthost, counter_block_interface, trim_counter_params['block_queue'], - trim_counter_params['trim_buffer_profiles']['downlink']) + created_block_profiles.add( + set_buffer_profile_for_block_queue(duthost, counter_block_interface, trim_counter_params['block_queue'], + trim_counter_params['trim_buffer_profiles']['downlink'])) + block_queue_bindings.append((counter_block_interface, trim_counter_params['block_queue'])) with allure.step("Create scheduler used for blocking egress queues"): create_blocking_scheduler(duthost) @@ -256,6 +272,23 @@ def setup_trimming(duthost, test_params, trim_counter_params, request): for buffer_profile in trim_counter_params['trim_buffer_profiles']: configure_trimming_action(duthost, trim_counter_params['trim_buffer_profiles'][buffer_profile], "off") + with allure.step("Remove trim queue buffer profile references"): + # config load merges the backup and does not delete BUFFER_QUEUE keys added on + # platforms whose base config lacks them (e.g. VPP). Clear them before deleting + # the profile so teardown does not leave a dangling BUFFER_QUEUE -> BUFFER_PROFILE + # leafref that fails post-test YANG validation. + delete_buffer_queue_for_trim_queue(duthost, test_params['egress_ports'][0]['dut_members']) + if len(test_params['egress_ports']) > 1: + delete_buffer_queue_for_trim_queue(duthost, test_params['egress_ports'][1]['dut_members']) + + with allure.step("Remove blocking-queue buffer config added during setup"): + # Same rationale as the trim queue: clear the BUFFER_QUEUE bindings this fixture + # added and delete the buffer profiles it created on demand, so teardown leaves a + # clean, YANG-valid CONFIG_DB on platforms (e.g. VPP) whose base config lacks them. + for interfaces, block_queue_id in block_queue_bindings: + delete_buffer_queue_for_block_queue(duthost, interfaces, block_queue_id) + delete_created_block_queue_buffer_profiles(duthost, created_block_profiles) + with allure.step("Delete trim queue test buffer profile"): delete_trim_queue_test_buffer_profile(duthost) diff --git a/tests/packet_trimming/packet_trimming_helper.py b/tests/packet_trimming/packet_trimming_helper.py index 39fb3c5fabd..5a8828b7979 100644 --- a/tests/packet_trimming/packet_trimming_helper.py +++ b/tests/packet_trimming/packet_trimming_helper.py @@ -30,7 +30,7 @@ MIRROR_SESSION_SRC_IP, MIRROR_SESSION_DST_IP, MIRROR_SESSION_DSCP, MIRROR_SESSION_TTL, MIRROR_SESSION_GRE, MIRROR_SESSION_QUEUE, SCHEDULER_CIR, SCHEDULER_METER_TYPE, PACKET_SIZE_MARGIN, - TRIMMING_COUNTER_INTERVAL, + TRIMMING_COUNTER_INTERVAL, DYNAMIC_TH, QUEUE_LEVEL_TRIM_SENT_DROP_SUPPORTED_PLATFORMS) from tests.packet_trimming.packet_trimming_config import PacketTrimmingConfig @@ -1574,6 +1574,39 @@ def cleanup_trimming_acl(duthost): logger.info("ACL rules cleanup completed successfully") +def ensure_block_queue_buffer_profile(duthost, block_queue_profile): + """ + Ensure the buffer profile referenced by a blocking queue exists in CONFIG_DB. + + The suite references per-queue profiles named ``queue__lossy_profile``. + On the SKUs packet trimming normally targets (Mellanox SN5640, Arista 7060X6) those + profiles are supplied by the platform ``buffers_defaults_objects.j2`` template. Minimal + buffer configurations - such as the VPP virtual switch on the shared Force10-S6000 hwsku - + do not define them, so the ``BUFFER_QUEUE`` reference would dangle and the subsequent + ``config mmu -p -t on`` trimming-action configuration would fail. Create a lossy + profile bound to the egress lossy pool on demand when the profile is missing. This is a + no-op on platforms that already define the profile, so behavior on those SKUs is unchanged. + + Args: + duthost: DUT host object + block_queue_profile (str): Buffer profile name referenced by the blocking queue + + Returns: + bool: True if this call created the profile (so teardown should remove it), + False if the platform already defined it. + """ + exists = duthost.shell( + f"redis-cli -n 4 exists 'BUFFER_PROFILE|{block_queue_profile}'")["stdout"].strip() + if exists == "1": + return False + + pool = TRIM_QUEUE_PROFILE_CONFIG["pool"] + fields = f"pool {pool} size 1518 dynamic_th {DYNAMIC_TH}" + duthost.shell(f"redis-cli -n 4 hset 'BUFFER_PROFILE|{block_queue_profile}' {fields}") + logger.info(f"Created missing blocking-queue buffer profile '{block_queue_profile}': {fields}") + return True + + def set_buffer_profile_for_block_queue(duthost, interfaces, block_queue_id, block_queue_profile): """ Set buffer profile for the blocked queue of interfaces. @@ -1584,6 +1617,10 @@ def set_buffer_profile_for_block_queue(duthost, interfaces, block_queue_id, bloc block_queue_id: Queue index used for blocking traffic block_queue_profile (str): Buffer profile name to apply for blocking queue + Returns: + str or None: The buffer profile name if this call created it on demand (so teardown + must delete it), otherwise None (the platform already defined it). + Raises: RuntimeError: If any interface fails to be configured with the specified profile. """ @@ -1593,6 +1630,10 @@ def set_buffer_profile_for_block_queue(duthost, interfaces, block_queue_id, bloc logger.info(f"Setting blocking queue ({block_queue_id}) buffer profile to '{block_queue_profile}', " f"ports: {interfaces}") + # Create the referenced buffer profile when the platform does not already define it + # (for example the VPP virtual switch), so blocking-queue setup is self-sufficient. + created_profile = ensure_block_queue_buffer_profile(duthost, block_queue_profile) + # Convert single interface to list if isinstance(interfaces, str): interfaces = [interfaces] @@ -1612,6 +1653,53 @@ def set_buffer_profile_for_block_queue(duthost, interfaces, block_queue_id, bloc raise RuntimeError(f"Exception while configuring interface {interface} blocking queue: {str(e)}") from e raise + return block_queue_profile if created_profile else None + + +def delete_buffer_queue_for_block_queue(duthost, interfaces, block_queue_id): + """ + Remove the BUFFER_QUEUE entries created for a blocking queue during setup. + + Mirrors ``delete_buffer_queue_for_trim_queue``: ``config load`` of the pre-test backup + merges rather than replaces, so BUFFER_QUEUE keys the test added on platforms whose base + configuration does not define them (for example the VPP virtual switch on the shared + Force10-S6000 hwsku) survive teardown and dangle once the on-demand blocking-queue profile + is removed. Delete them explicitly, before the profile is removed, so teardown leaves a + YANG-valid configuration on every platform. Platforms whose base configuration defines the + queue restore it from the backup on ``config load``, so behavior there is unchanged. + + Args: + duthost: DUT host object + interfaces (list or str): Port names whose blocking-queue reference should be removed + block_queue_id: Queue index used for blocking traffic + """ + block_queue_id = str(block_queue_id) + + if isinstance(interfaces, str): + interfaces = [interfaces] + + for interface in interfaces: + duthost.shell(f"redis-cli -n 4 del 'BUFFER_QUEUE|{interface}|{block_queue_id}'") + logger.info(f"Removed blocking-queue BUFFER_QUEUE|{interface}|{block_queue_id} during teardown") + + +def delete_created_block_queue_buffer_profiles(duthost, profiles): + """ + Delete the blocking-queue buffer profiles created on demand during setup. + + Only profiles that ``set_buffer_profile_for_block_queue`` created (platforms whose base + configuration lacked them, such as the VPP virtual switch) are passed here; on SKUs that + already define these profiles nothing is created and nothing is deleted, so behavior there + is unchanged. Removing them keeps CONFIG_DB clean and preserves test isolation across runs. + + Args: + duthost: DUT host object + profiles (set or list): Buffer profile names created during setup + """ + for profile in sorted(set(p for p in profiles if p)): + duthost.shell(f"redis-cli -n 4 del 'BUFFER_PROFILE|{profile}'") + logger.info(f"Deleted on-demand blocking-queue buffer profile '{profile}' during teardown") + def create_trim_queue_test_buffer_profile(duthost): """ @@ -1675,6 +1763,35 @@ def set_buffer_profile_for_trim_queue(duthost, interfaces, trim_queue_id=None, t raise +def delete_buffer_queue_for_trim_queue(duthost, interfaces, trim_queue_id=None): + """ + Remove the BUFFER_QUEUE entries created for the trimming queue during setup. + + Teardown restores configuration with ``config load``, which merges the pre-test + backup and does not delete BUFFER_QUEUE keys the test added on platforms whose base + configuration does not already define them (for example the VPP virtual switch on + the shared Force10-S6000 hwsku). Once ``trim_queue_test_profile`` is deleted, those + leftover keys become a dangling BUFFER_QUEUE -> BUFFER_PROFILE leafref that fails the + post-test YANG validation. Delete the entries explicitly, before the profile is + removed, so teardown leaves a YANG-valid configuration on every platform. Platforms + whose base configuration defines the queue restore it from the backup on ``config + load``, so behavior there is unchanged. + + Args: + duthost: DUT host object + interfaces (list or str): Port names whose trimming-queue reference should be removed + trim_queue_id (int): Queue index used for packet trimming (default: trim queue from config) + """ + trim_queue_id = str(trim_queue_id) if trim_queue_id else str(PacketTrimmingConfig.get_trim_queue(duthost)) + + if isinstance(interfaces, str): + interfaces = [interfaces] + + for interface in interfaces: + duthost.shell(f"redis-cli -n 4 del 'BUFFER_QUEUE|{interface}|{trim_queue_id}'") + logger.info(f"Removed trimming-queue BUFFER_QUEUE|{interface}|{trim_queue_id} during teardown") + + def prepare_service_port(duthost, service_port): """ Prepare service port for packet trimming tests by checking existence,