From ae048e702181846fbfdf5aeb35c84c21c199d3d1 Mon Sep 17 00:00:00 2001 From: mssonicbld <79238446+mssonicbld@users.noreply.github.com> Date: Wed, 3 Jun 2026 04:14:01 +0800 Subject: [PATCH] Add remove operation for buffer_profile if remove entire profile. (#4585) #### What I did Add remove operation to support entire profile object removal, which complains in this issue: https://github.com/sonic-net/sonic-buildimage/issues/23907 #### How I did it Check the path in patch, if it proceed with entire object, then we allow remove operation for object level, otherwise follow current field level validator logic. #### How to verify it Test it in testbed. Signed-off-by: Sonic Build Admin #### Previous command output (if the output of a command-line utility has changed) #### New command output (if the output of a command-line utility has changed) --- .../field_operation_validators.py | 24 ++++++++++ .../field_operation_validator_test.py | 44 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/generic_config_updater/field_operation_validators.py b/generic_config_updater/field_operation_validators.py index 9291b8240..361abd991 100644 --- a/generic_config_updater/field_operation_validators.py +++ b/generic_config_updater/field_operation_validators.py @@ -171,6 +171,30 @@ def rdma_config_update_validator(scope, patch_element): def buffer_profile_config_update_validator(scope, patch_element): + """ + Enhanced buffer profile validator that handles both field-level and object-level operations. + - Field-level operations (e.g., /BUFFER_PROFILE/profile/dynamic_th) follow existing rules + - Object-level operations (e.g., /BUFFER_PROFILE/profile) allow remove operations + """ + path = patch_element["path"] + path_parts = jsonpointer.JsonPointer(path).parts + + # Determine if this is an object-level operation (entire profile) or field-level operation + # Object-level: /BUFFER_PROFILE/profile_name (2 parts) + # Field-level: /BUFFER_PROFILE/profile_name/field_name (3+ parts) + is_object_level = len(path_parts) == 2 # table + profile_name only + + if is_object_level: + # For object-level operations, we're more permissive + # Allow add/remove/replace operations for entire profile objects + allowed_object_operations = ['add', 'remove', 'replace'] + + if patch_element['op'] in allowed_object_operations: + return True # Allow object-level operations + else: + return False # Disallow unsupported operations + + # For field-level operations, use the existing validation logic return rdma_config_update_validator_common(scope, patch_element) diff --git a/tests/generic_config_updater/field_operation_validator_test.py b/tests/generic_config_updater/field_operation_validator_test.py index ca7b3acf3..866e04015 100644 --- a/tests/generic_config_updater/field_operation_validator_test.py +++ b/tests/generic_config_updater/field_operation_validator_test.py @@ -273,6 +273,50 @@ def test_buffer_profile_config_update_validator(self, scope, asic, field, value, ): assert fov.buffer_profile_config_update_validator(scope, patch_element) is True + def test_buffer_profile_config_update_validator_object_level_remove(self): + """Test that object-level remove operations are allowed (fixes rollback issue)""" + patch_element = { + "path": "/BUFFER_PROFILE/pg_lossless_40000_5m_profile", + "op": "remove" + } + + # Object-level remove should be allowed without ASIC/version validation + assert fov.buffer_profile_config_update_validator("localhost", patch_element) is True + + def test_buffer_profile_config_update_validator_object_level_add(self): + """Test that object-level add operations are allowed""" + patch_element = { + "path": "/BUFFER_PROFILE/new_profile", + "op": "add", + "value": {"size": "1024", "pool": "ingress_lossless_pool"} + } + + # Object-level add should be allowed + assert fov.buffer_profile_config_update_validator("localhost", patch_element) is True + + def test_buffer_profile_config_update_validator_object_level_unsupported_op(self): + """Test that unsupported operations on object-level are denied""" + patch_element = { + "path": "/BUFFER_PROFILE/my_profile", + "op": "move", # Unsupported operation + "from": "/BUFFER_PROFILE/old_profile" + } + + assert fov.buffer_profile_config_update_validator("localhost", patch_element) is False + + def test_buffer_profile_config_update_validator_field_level_uses_existing_validation(self): + """Test that field-level operations use existing validation logic""" + patch_element = { + "path": "/BUFFER_PROFILE/my_profile/dynamic_th", + "op": "replace", + "value": "2" + } + + # Mock the existing validation to return True + with patch("generic_config_updater.field_operation_validators.rdma_config_update_validator_common", + return_value=True): + assert fov.buffer_profile_config_update_validator("localhost", patch_element) is True + @patch("sonic_py_common.device_info.get_sonic_version_info", mock.Mock(return_value={"build_version": "SONiC.20220530"})) @patch("generic_config_updater.field_operation_validators.get_asic_name",