From f683fedc4aa27a366f1faf01f71b7a7827854d8b Mon Sep 17 00:00:00 2001 From: Sonic Build Admin Date: Wed, 1 Oct 2025 18:57:23 +0000 Subject: [PATCH] [Smartswitch][DPU]Add multiple key handling #### Description Main Logic Addition (sonic-chassisd/scripts/chassisd): Added a new condition check in the DpuStateManagerTask class The logic checks if there's a non-empty key AND the database is not 'CHASSIS_STATE_DB' When both conditions are true, it sets update_required = True and breaks out of the loop #### Motivation and Context When there is updates to multiple DBs at the same time, usually if there is update to CHASSIS_STATE_DB the update_required is set to false, this change is added to handle this case #### How Has This Been Tested? ``` tests/test_dpu_chassisd.py::test_dpu_state_manager_update_required_logic PASSED [100%] ``` Manual test to shutdown interface/service and confirm that the data plane/control plane interfaces are set to down #### Additional Information (Optional) --- sonic-chassisd/scripts/chassisd | 4 + sonic-chassisd/tests/test_dpu_chassisd.py | 98 +++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/sonic-chassisd/scripts/chassisd b/sonic-chassisd/scripts/chassisd index 2d29805..f51db72 100755 --- a/sonic-chassisd/scripts/chassisd +++ b/sonic-chassisd/scripts/chassisd @@ -1565,6 +1565,10 @@ class DpuStateManagerTask(ProcessTaskBase): update_required = False continue self.logger.log_info(f"DPU_STATE change detected: operation={op}, key={key}") + elif key: + # If there is any change in the non-DPU_STATE table, we need to update the state + update_required = True + break if update_required: [self.current_dp_state, self.current_cp_state] = self.dpu_state_updater.update_state() diff --git a/sonic-chassisd/tests/test_dpu_chassisd.py b/sonic-chassisd/tests/test_dpu_chassisd.py index 39e5665..118c7df 100644 --- a/sonic-chassisd/tests/test_dpu_chassisd.py +++ b/sonic-chassisd/tests/test_dpu_chassisd.py @@ -4,6 +4,8 @@ import pytest import signal import threading +import time +from datetime import datetime from imp import load_source import re @@ -455,3 +457,99 @@ def hset(key, field, value): # Verify current states were updated assert dpu_state_mng.current_dp_state == 'up' assert dpu_state_mng.current_cp_state == 'down' + + +def test_dpu_state_manager_update_required_logic(): + """Test that DpuStateManagerTask correctly sets update_required based on various conditions""" + chassis = MockDpuChassis() + chassis.get_dpu_id = MagicMock(return_value=0) + chassis.get_dataplane_state = MagicMock(return_value=True) + chassis.get_controlplane_state = MagicMock(return_value=True) + + chassis_state_db = {} + update_count = 0 + + def hset(key, field, value): + nonlocal update_count + update_count += 1 + if key not in chassis_state_db: + chassis_state_db[key] = {} + chassis_state_db[key][field] = value + + # Test case 1: update_required should be True when there are changes in non-DPU_STATE tables + with mock.patch.object(swsscommon.Table, 'hset', side_effect=hset): + # Create mock selectables + mock_selectable_app_db = MagicMock() + mock_selectable_app_db.getDbConnector.return_value.getDbName.return_value = 'APPL_DB' + mock_selectable_app_db.pop.return_value = ('PORT_TABLE_KEY', 'SET', None) + + mock_selectable_state_db = MagicMock() + mock_selectable_state_db.getDbConnector.return_value.getDbName.return_value = 'STATE_DB' + mock_selectable_state_db.pop.return_value = None + + mock_selectable_chassis_state_db = MagicMock() + mock_selectable_chassis_state_db.getDbConnector.return_value.getDbName.return_value = 'CHASSIS_STATE_DB' + mock_selectable_chassis_state_db.pop.return_value = None + + # Mock the SubscriberStateTable constructor to return our mock selectables + with mock.patch.object(swsscommon, 'SubscriberStateTable', side_effect=[ + mock_selectable_app_db, # PORT_TABLE + mock_selectable_state_db, # SYSTEM_READY + mock_selectable_chassis_state_db # DPU_STATE + ]): + with mock.patch.object(swsscommon.Select, 'select', + side_effect=[(swsscommon.Select.OBJECT, None), KeyboardInterrupt]): + + dpu_updater = DpuStateUpdater(SYSLOG_IDENTIFIER, chassis) + dpu_updater._time_now = MagicMock(return_value='Sat Jan 01 12:00:00 AM UTC 2000') + + dpu_state_mng = DpuStateManagerTask(SYSLOG_IDENTIFIER, dpu_updater) + dpu_state_mng.current_dp_state = 'up' + dpu_state_mng.current_cp_state = 'up' + + dpu_state_mng.task_worker() + + # Verify state was updated since update_required should be True + assert update_count > 0 + + # Reset for test case 2 + update_count = 0 + chassis_state_db = {} + + # Test case 2: update_required should be True when pop returns multiple values + # and one key returns STATE_DB and another CHASSIS_STATE_DB + with mock.patch.object(swsscommon.Table, 'hset', side_effect=hset): + # Create mock selectables with different database names + mock_selectable_app_db = MagicMock() + mock_selectable_app_db.getDbConnector.return_value.getDbName.return_value = 'APPL_DB' + mock_selectable_app_db.pop.return_value = None + + mock_selectable_state_db = MagicMock() + mock_selectable_state_db.getDbConnector.return_value.getDbName.return_value = 'STATE_DB' + mock_selectable_state_db.pop.return_value = ('SYSTEM_READY_KEY', 'SET', (('Status', 'UP'), ('Other', 'Value'))) + + mock_selectable_chassis_state_db = MagicMock() + mock_selectable_chassis_state_db.getDbConnector.return_value.getDbName.return_value = 'CHASSIS_STATE_DB' + mock_selectable_chassis_state_db.pop.return_value = None + + # Mock the SubscriberStateTable constructor to return our mock selectables + with mock.patch.object(swsscommon, 'SubscriberStateTable', side_effect=[ + mock_selectable_app_db, # PORT_TABLE + mock_selectable_state_db, # SYSTEM_READY + mock_selectable_chassis_state_db # DPU_STATE + ]): + with mock.patch.object(swsscommon.Select, 'select', + side_effect=[(swsscommon.Select.OBJECT, None), KeyboardInterrupt]): + + dpu_updater = DpuStateUpdater(SYSLOG_IDENTIFIER, chassis) + dpu_updater._time_now = MagicMock(return_value='Sat Jan 01 12:00:00 AM UTC 2000') + + dpu_state_mng = DpuStateManagerTask(SYSLOG_IDENTIFIER, dpu_updater) + dpu_state_mng.current_dp_state = 'up' + dpu_state_mng.current_cp_state = 'up' + + dpu_state_mng.task_worker() + + # Verify state was updated since update_required should be True for multiple values + # even with mixed STATE_DB and CHASSIS_STATE_DB + assert update_count > 0