diff --git a/config/chassis_modules.py b/config/chassis_modules.py index ee8d6abd6a..4f71ada802 100755 --- a/config/chassis_modules.py +++ b/config/chassis_modules.py @@ -6,6 +6,7 @@ import subprocess import utilities_common.cli as clicommon from utilities_common.chassis import is_smartswitch, get_all_dpus +from utilities_common.module import ModuleHelper from datetime import datetime, timedelta, timezone TIMEOUT_SECS = 10 @@ -64,50 +65,6 @@ def get_config_module_state(db, chassis_module_name): else: return fvs['admin_status'] - -def get_state_transition_in_progress(db, chassis_module_name): - ensure_statedb_connected(db) - fvs = db.statedb.get_entry('CHASSIS_MODULE_TABLE', chassis_module_name) - value = fvs.get('state_transition_in_progress', 'False') if fvs else 'False' - return value - - -def set_state_transition_in_progress(db, chassis_module_name, value): - ensure_statedb_connected(db) - state_db = db.statedb - entry = state_db.get_entry('CHASSIS_MODULE_TABLE', chassis_module_name) or {} - entry['state_transition_in_progress'] = value - if value == 'True': - entry['transition_start_time'] = datetime.now(timezone.utc).isoformat() - else: - # Remove transition_start_time from both local entry and database - entry.pop('transition_start_time', None) - state_db.delete_field('CHASSIS_MODULE_TABLE', chassis_module_name, 'transition_start_time') - state_db.set_entry('CHASSIS_MODULE_TABLE', chassis_module_name, entry) - - -def is_transition_timed_out(db, chassis_module_name): - ensure_statedb_connected(db) - state_db = db.statedb - fvs = state_db.get_entry('CHASSIS_MODULE_TABLE', chassis_module_name) - if not fvs: - return False - start_time_str = fvs.get('transition_start_time') - if not start_time_str: - return False - try: - start_time = datetime.fromisoformat(start_time_str) - except ValueError: - return False - - # Use UTC everywhere for consistent comparison - current_time = datetime.now(timezone.utc) - if start_time.tzinfo is None: - # If stored time is naive, assume it's UTC - start_time = start_time.replace(tzinfo=timezone.utc) - - return current_time - start_time > TRANSITION_TIMEOUT - # # Name: check_config_module_state_with_timeout # return: True: timeout, False: not timeout @@ -196,15 +153,10 @@ def shutdown_chassis_module(db, chassis_module_name): return if is_smartswitch(): - if get_state_transition_in_progress(db, chassis_module_name) == 'True': - if is_transition_timed_out(db, chassis_module_name): - set_state_transition_in_progress(db, chassis_module_name, 'False') - click.echo(f"Previous transition for module {chassis_module_name} timed out. Proceeding with shutdown.") - else: - click.echo(f"Module {chassis_module_name} state transition is already in progress") - return - else: - set_state_transition_in_progress(db, chassis_module_name, 'True') + module_helper = ModuleHelper() + if module_helper.get_module_state_transition(chassis_module_name): + click.echo(f"Module {chassis_module_name} state transition is already in progress") + return click.echo(f"Shutting down chassis module {chassis_module_name}") fvs = { @@ -243,15 +195,10 @@ def startup_chassis_module(db, chassis_module_name): return if is_smartswitch(): - if get_state_transition_in_progress(db, chassis_module_name) == 'True': - if is_transition_timed_out(db, chassis_module_name): - set_state_transition_in_progress(db, chassis_module_name, 'False') - click.echo(f"Previous transition for module {chassis_module_name} timed out. Proceeding with startup.") - else: - click.echo(f"Module {chassis_module_name} state transition is already in progress") - return - else: - set_state_transition_in_progress(db, chassis_module_name, 'True') + module_helper = ModuleHelper() + if module_helper.get_module_state_transition(chassis_module_name): + click.echo(f"Module {chassis_module_name} state transition is already in progress") + return click.echo(f"Starting up chassis module {chassis_module_name}") fvs = { diff --git a/scripts/reboot_smartswitch_helper b/scripts/reboot_smartswitch_helper index 62b7dd61b4..ba93b3ff99 100644 --- a/scripts/reboot_smartswitch_helper +++ b/scripts/reboot_smartswitch_helper @@ -6,6 +6,9 @@ declare -r MODULE_REBOOT_SMARTSWITCH="SMARTSWITCH" declare -r EXIT_DPU_DOWN=2 +# Set default platform JSON path if not already set +PLATFORM_JSON_PATH=${PLATFORM_JSON_PATH:-"/usr/share/sonic/platform/platform.json"} + # Function to print debug message function log_message() { local message=$1 @@ -71,7 +74,6 @@ function get_reboot_status() function module_pre_shutdown() { local DPU_NAME=$1 - local DPU_BUS_INFO=$2 python3 -c "from utilities_common.module import ModuleHelper; helper = ModuleHelper(); helper.module_pre_shutdown('${DPU_NAME}')" if [ $? -ne 0 ]; then log_message "ERROR: Module pre-shutdown vendor API failed" @@ -82,13 +84,46 @@ function module_pre_shutdown() function module_post_startup() { local DPU_NAME=$1 - local DPU_BUS_INFO=$2 python3 -c "from utilities_common.module import ModuleHelper; helper = ModuleHelper(); helper.module_post_startup('${DPU_NAME}')" if [ $? -ne 0 ]; then log_message "ERROR: Module post-startup vendor API failed" fi } +# Function to set state_transition_in_progress flag +function set_module_state_transition_flag() +{ + local DPU_NAME=$1 + local FLAG_VALUE=$2 + python3 -c "from utilities_common.module import ModuleHelper; helper = ModuleHelper(); helper.set_module_state_transition('${DPU_NAME}', ${FLAG_VALUE})" + if [ $? -ne 0 ]; then + log_message "ERROR: Setting module state transition flag failed" + fi +} + +# Function to clear state_transition_in_progress flag +function clear_module_state_transition_flag() +{ + local DPU_NAME=$1 + python3 -c "from utilities_common.module import ModuleHelper; helper = ModuleHelper(); helper.clear_module_state_transition('${DPU_NAME}')" + if [ $? -ne 0 ]; then + log_message "ERROR: Clearing module state transition flag failed" + fi +} + +# Function to get state_transition_in_progress flag +function get_module_state_transition_flag() +{ + local DPU_NAME=$1 + local result + result=$(python3 -c "from utilities_common.module import ModuleHelper; helper = ModuleHelper(); print(helper.get_module_state_transition('${DPU_NAME}'))") + if [ "$result" == "True" ]; then + return 0 # True in bash (success) + else + return 1 # False in bash (failure) + fi +} + # Function to reboot DPU function reboot_dpu_platform() { @@ -171,7 +206,7 @@ function reboot_dpu() local REBOOT_TYPE=$2 local DPU_INDEX=${DPU_NAME//[!0-9]/} - debug "User requested rebooting device ${DPU_NAME} ..." + log_message "User requested rebooting device ${DPU_NAME} ..." # Check if the DPU operation status is online before rebooting local oper_status @@ -187,6 +222,16 @@ function reboot_dpu() fi fi + if [[ "$REBOOT_TYPE" != $MODULE_REBOOT_SMARTSWITCH ]]; then + # get and set the state_transition_in_progress flag before reboot + if ! get_module_state_transition_flag "${DPU_NAME}"; then + set_module_state_transition_flag "${DPU_NAME}" True + else + log_message "ERROR: state_transition_in_progress flag is already set for ${DPU_NAME}" + return ${EXIT_ERROR} + fi + fi + # Send reboot command to DPU gnmi_reboot_dpu "${DPU_NAME}" if [ $? -ne 0 ]; then @@ -199,20 +244,21 @@ function reboot_dpu() return ${EXIT_ERROR} fi - module_pre_shutdown ${DPU_NAME} ${DPU_BUS_INFO} + module_pre_shutdown ${DPU_NAME} if [ $? -ne 0 ]; then - log_message "ERROR: Failed to detach PCI module for ${DPU_NAME}" + log_message "ERROR: Failed to pre-shutdown the module for ${DPU_NAME}" return ${EXIT_ERROR} fi reboot_dpu_platform ${DPU_NAME} ${REBOOT_TYPE} - if [ $? -ne 0 ]; then - log_message "ERROR: Failed to send platform command to reboot ${DPU_NAME}" - return ${EXIT_ERROR} - fi if [[ "$REBOOT_TYPE" != $MODULE_REBOOT_SMARTSWITCH ]]; then - module_post_startup ${DPU_NAME} ${DPU_BUS_INFO} + module_post_startup ${DPU_NAME} + if [ $? -ne 0 ]; then + log_message "ERROR: Failed to startup the module for ${DPU_NAME}" + return ${EXIT_ERROR} + fi + clear_module_state_transition_flag ${DPU_NAME} fi } diff --git a/tests/chassis_modules_test.py b/tests/chassis_modules_test.py index 1a5a67e201..b67f64a60b 100755 --- a/tests/chassis_modules_test.py +++ b/tests/chassis_modules_test.py @@ -3,8 +3,7 @@ from click.testing import CliRunner from datetime import datetime, timedelta, timezone from config.chassis_modules import ( - set_state_transition_in_progress, - is_transition_timed_out, + StateDBHelper, TRANSITION_TIMEOUT ) @@ -448,9 +447,10 @@ def test_show_and_verify_system_lags_output_lc4(self): assert return_code == 0 assert result == show_chassis_system_lags_output_lc4 - def test_shutdown_triggers_transition_tracking(self): + def test_shutdown_smartswitch_module(self): with mock.patch("config.chassis_modules.is_smartswitch", return_value=True), \ - mock.patch("config.chassis_modules.get_config_module_state", return_value='up'): + mock.patch("config.chassis_modules.get_config_module_state", return_value='up'), \ + mock.patch("config.chassis_modules.ModuleHelper.get_module_state_transition", return_value=False): runner = CliRunner() db = Db() @@ -462,6 +462,7 @@ def test_shutdown_triggers_transition_tracking(self): print(result.exit_code) print(result.output) assert result.exit_code == 0 + assert "Shutting down chassis module DPU0" in result.output # Check CONFIG_DB for admin_status cfg_fvs = db.cfgdb.get_entry("CHASSIS_MODULE", "DPU0") @@ -469,31 +470,33 @@ def test_shutdown_triggers_transition_tracking(self): print(f"admin_status: {admin_status}") assert admin_status == "down" - # Check STATE_DB for transition flags - state_fvs = db.db.get_all("STATE_DB", "CHASSIS_MODULE_TABLE|DPU0") - transition_flag = state_fvs.get("state_transition_in_progress") - transition_time = state_fvs.get("transition_start_time") + def test_shutdown_smartswitch_transition_in_progress(self): + with mock.patch("config.chassis_modules.is_smartswitch", return_value=True), \ + mock.patch("config.chassis_modules.get_config_module_state", return_value='up'), \ + mock.patch("config.chassis_modules.ModuleHelper.get_module_state_transition", return_value=True): - print(f"state_transition_in_progress: {transition_flag}") - print(f"transition_start_time: {transition_time}") + runner = CliRunner() + db = Db() + result = runner.invoke( + config.config.commands["chassis"].commands["modules"].commands["shutdown"], + ["DPU0"], + obj=db + ) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert "Module DPU0 state transition is already in progress" in result.output - assert transition_flag == "True" - assert transition_time is not None + # Verify no config change was made + cfg_fvs = db.cfgdb.get_entry("CHASSIS_MODULE", "DPU0") + assert cfg_fvs == {} - def test_shutdown_triggers_transition_in_progress(self): + def test_shutdown_module_already_down(self): with mock.patch("config.chassis_modules.is_smartswitch", return_value=True), \ - mock.patch("config.chassis_modules.get_config_module_state", return_value='up'): + mock.patch("config.chassis_modules.get_config_module_state", return_value='down'): runner = CliRunner() db = Db() - - fvs = { - 'admin_status': 'up', - 'state_transition_in_progress': 'True', - 'transition_start_time': datetime.now(timezone.utc).isoformat() - } - db.cfgdb.set_entry('CHASSIS_MODULE', "DPU0", fvs) - result = runner.invoke( config.config.commands["chassis"].commands["modules"].commands["shutdown"], ["DPU0"], @@ -502,41 +505,55 @@ def test_shutdown_triggers_transition_in_progress(self): print(result.exit_code) print(result.output) assert result.exit_code == 0 + assert "Module DPU0 is already in down state" in result.output - fvs = db.db.get_all("STATE_DB", "CHASSIS_MODULE_TABLE|DPU0") - print(f"state_transition_in_progress:{fvs['state_transition_in_progress']}") - print(f"transition_start_time:{fvs['transition_start_time']}") - - def test_shutdown_triggers_transition_timeout(self): + def test_startup_smartswitch_module(self): with mock.patch("config.chassis_modules.is_smartswitch", return_value=True), \ - mock.patch("config.chassis_modules.get_config_module_state", return_value='up'): + mock.patch("config.chassis_modules.get_config_module_state", return_value='down'), \ + mock.patch("config.chassis_modules.ModuleHelper.get_module_state_transition", return_value=False): runner = CliRunner() db = Db() + result = runner.invoke( + config.config.commands["chassis"].commands["modules"].commands["startup"], + ["DPU0"], + obj=db + ) + print(result.exit_code) + print(result.output) + assert result.exit_code == 0 + assert "Starting up chassis module DPU0" in result.output + + # Check CONFIG_DB for admin_status + cfg_fvs = db.cfgdb.get_entry("CHASSIS_MODULE", "DPU0") + admin_status = cfg_fvs.get("admin_status") + print(f"admin_status: {admin_status}") + assert admin_status == "up" - fvs = { - 'admin_status': 'up', - 'state_transition_in_progress': 'True', - 'transition_start_time': (datetime.now(timezone.utc) - timedelta(minutes=30)).isoformat() - } - db.cfgdb.set_entry('CHASSIS_MODULE', "DPU0", fvs) + def test_startup_smartswitch_transition_in_progress(self): + with mock.patch("config.chassis_modules.is_smartswitch", return_value=True), \ + mock.patch("config.chassis_modules.get_config_module_state", return_value='down'), \ + mock.patch("config.chassis_modules.ModuleHelper.get_module_state_transition", return_value=True): + runner = CliRunner() + db = Db() result = runner.invoke( - config.config.commands["chassis"].commands["modules"].commands["shutdown"], + config.config.commands["chassis"].commands["modules"].commands["startup"], ["DPU0"], obj=db ) print(result.exit_code) print(result.output) assert result.exit_code == 0 + assert "Module DPU0 state transition is already in progress" in result.output - fvs = db.db.get_all("STATE_DB", "CHASSIS_MODULE_TABLE|DPU0") - print(f"state_transition_in_progress:{fvs['state_transition_in_progress']}") - print(f"transition_start_time:{fvs['transition_start_time']}") + # Verify no config change was made + cfg_fvs = db.cfgdb.get_entry("CHASSIS_MODULE", "DPU0") + assert cfg_fvs == {} - def test_startup_triggers_transition_tracking(self): + def test_startup_module_already_up(self): with mock.patch("config.chassis_modules.is_smartswitch", return_value=True), \ - mock.patch("config.chassis_modules.get_config_module_state", return_value='down'): + mock.patch("config.chassis_modules.get_config_module_state", return_value='up'): runner = CliRunner() db = Db() @@ -548,79 +565,7 @@ def test_startup_triggers_transition_tracking(self): print(result.exit_code) print(result.output) assert result.exit_code == 0 - - fvs = db.db.get_all("STATE_DB", "CHASSIS_MODULE_TABLE|DPU0") - print(f"state_transition_in_progress:{fvs['state_transition_in_progress']}") - print(f"transition_start_time:{fvs['transition_start_time']}") - - def test_set_state_transition_in_progress_sets_and_removes_timestamp(self): - db = mock.MagicMock() - db.statedb = mock.MagicMock() - - # Case 1: Set to 'True' adds timestamp - db.statedb.get_entry.return_value = {} - set_state_transition_in_progress(db, "DPU0", "True") - args = db.statedb.set_entry.call_args[0] - updated_entry = args[2] - assert updated_entry["state_transition_in_progress"] == "True" - assert "transition_start_time" in updated_entry - - # Case 2: Set to 'False' removes timestamp - db.statedb.get_entry.return_value = { - "state_transition_in_progress": "True", - "transition_start_time": "2025-05-01T01:00:00" - } - set_state_transition_in_progress(db, "DPU0", "False") - args = db.statedb.set_entry.call_args[0] - updated_entry = args[2] - assert updated_entry["state_transition_in_progress"] == "False" - assert "transition_start_time" not in updated_entry - - def test_is_transition_timed_out_all_paths(self): - db = mock.MagicMock() - db.statedb = mock.MagicMock() - - # Case 1: No entry - db.statedb.get_entry.return_value = None - assert is_transition_timed_out(db, "DPU0") is False - - # Case 2: No transition_start_time - db.statedb.get_entry.return_value = {"state_transition_in_progress": "True"} - assert is_transition_timed_out(db, "DPU0") is False - - # Case 3: Invalid format - db.statedb.get_entry.return_value = {"transition_start_time": "not-a-date"} - assert is_transition_timed_out(db, "DPU0") is False - - # Case 4: Timed out - old_time = (datetime.now(timezone.utc) - TRANSITION_TIMEOUT - timedelta(seconds=1)).isoformat() - db.statedb.get_entry.return_value = {"transition_start_time": old_time} - assert is_transition_timed_out(db, "DPU0") is True - - # Case 5: Not timed out yet - now = datetime.now(timezone.utc).isoformat() - db.statedb.get_entry.return_value = {"transition_start_time": now} - assert is_transition_timed_out(db, "DPU0") is False - - def test_delete_field(self): - """Single test to cover missing delete_field and timezone handling lines""" - from config.chassis_modules import StateDBHelper - from datetime import timezone - - # Test delete_field method (covers lines 32-34) - mock_sonic_db = mock.MagicMock() - mock_redis_client = mock.MagicMock() - mock_sonic_db.get_redis_client.return_value = mock_redis_client - helper = StateDBHelper(mock_sonic_db) - helper.delete_field('TEST_TABLE', 'test_key', 'test_field') - mock_redis_client.hdel.assert_called_once_with("TEST_TABLE|test_key", "test_field") - - # Test timezone-aware datetime handling (covers line 109) - db = mock.MagicMock() - db.statedb = mock.MagicMock() - tz_time = (datetime.now(timezone.utc) - TRANSITION_TIMEOUT - timedelta(seconds=1)).isoformat() - db.statedb.get_entry.return_value = {"transition_start_time": tz_time} - assert is_transition_timed_out(db, "DPU0") is True + assert "Module DPU0 is already set to up state" in result.output @classmethod def teardown_class(cls): diff --git a/tests/test_module.py b/tests/test_module.py index 9d2ce1eea3..e29ef3389d 100644 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -161,3 +161,132 @@ def test_module_post_startup_operation_failed(self, result = module_helper.module_post_startup("DPU1") assert result is False mock_log_error.assert_called_once_with("Module post-startup status for module DPU1: False") + + def test_set_module_state_transition_success(self, + mock_load_platform_chassis, + mock_try_get_args, + mock_try_get): + mock_try_get_args.return_value = 1 + mock_try_get.return_value = True + mock_module = mock.MagicMock() + mock_module.set_module_state_transition.return_value = True + module_helper.platform_chassis.get_module.return_value = mock_module + + result = module_helper.set_module_state_transition("DPU1", True) + assert result is True + + def test_set_module_state_transition_invalid_index(self, + mock_load_platform_chassis, + mock_try_get_args): + mock_try_get_args.return_value = INVALID_MODULE_INDEX + + result = module_helper.set_module_state_transition("DPU1", True) + assert result is False + + def test_set_module_state_transition_method_not_found(self, + mock_load_platform_chassis, + mock_try_get_args, + mock_log_error): + mock_try_get_args.return_value = 1 + mock_module = object() + module_helper.platform_chassis.get_module.return_value = mock_module + + result = module_helper.set_module_state_transition("DPU1", True) + assert result is False + mock_log_error.assert_called_once_with("Set module state transition method not found in platform chassis") + + def test_set_module_state_transition_operation_failed(self, + mock_load_platform_chassis, + mock_try_get_args, + mock_try_get, + mock_log_error): + # First call returns valid module index, second call returns False (operation failed) + mock_try_get_args.side_effect = [1, False] + mock_module = mock.MagicMock() + mock_module.set_module_state_transition.return_value = False + module_helper.platform_chassis.get_module.return_value = mock_module + + result = module_helper.set_module_state_transition("DPU1", True) + assert result is False + mock_log_error.assert_called_once_with("Set module state transition flag status for module DPU1: False") + + def test_clear_module_state_transition_success(self, + mock_load_platform_chassis, + mock_try_get_args, + mock_try_get): + mock_try_get_args.return_value = 1 + mock_try_get.return_value = True + mock_module = mock.MagicMock() + mock_module.clear_module_state_transition.return_value = True + module_helper.platform_chassis.get_module.return_value = mock_module + + result = module_helper.clear_module_state_transition("DPU1") + assert result is True + + def test_clear_module_state_transition_invalid_index(self, + mock_load_platform_chassis, + mock_try_get_args): + mock_try_get_args.return_value = INVALID_MODULE_INDEX + + result = module_helper.clear_module_state_transition("DPU1") + assert result is False + + def test_clear_module_state_transition_method_not_found(self, + mock_load_platform_chassis, + mock_try_get_args, + mock_log_error): + mock_try_get_args.return_value = 1 + mock_module = object() + module_helper.platform_chassis.get_module.return_value = mock_module + + result = module_helper.clear_module_state_transition("DPU1") + assert result is False + mock_log_error.assert_called_once_with("Clear module state transition method not found in platform chassis") + + def test_clear_module_state_transition_operation_failed(self, + mock_load_platform_chassis, + mock_try_get_args, + mock_try_get, + mock_log_error): + # First call returns valid module index, second call returns False (operation failed) + mock_try_get_args.side_effect = [1, False] + mock_module = mock.MagicMock() + mock_module.clear_module_state_transition.return_value = False + module_helper.platform_chassis.get_module.return_value = mock_module + + result = module_helper.clear_module_state_transition("DPU1") + assert result is False + mock_log_error.assert_called_once_with("Clear module state transition flag status for module DPU1: False") + + def test_get_module_state_transition_success(self, + mock_load_platform_chassis, + mock_try_get_args, + mock_try_get): + # First call returns valid module index, second call returns True (operation result) + mock_try_get_args.side_effect = [1, True] + mock_module = mock.MagicMock() + mock_module.get_module_state_transition.return_value = True + module_helper.platform_chassis.get_module.return_value = mock_module + + result = module_helper.get_module_state_transition("DPU1") + assert result is True + + def test_get_module_state_transition_invalid_index(self, + mock_load_platform_chassis, + mock_try_get_args): + mock_try_get_args.return_value = INVALID_MODULE_INDEX + + result = module_helper.get_module_state_transition("DPU1") + assert result is False + + def test_get_module_state_transition_method_not_found(self, + mock_load_platform_chassis, + mock_try_get_args, + mock_log_error): + mock_try_get_args.return_value = 1 + mock_module = object() + module_helper.platform_chassis.get_module.return_value = mock_module + + result = module_helper.get_module_state_transition("DPU1") + assert result is False + mock_log_error.assert_called_once_with("Get module state transition method not found in platform chassis") diff --git a/utilities_common/module.py b/utilities_common/module.py index 6bc7377709..03f211d97c 100644 --- a/utilities_common/module.py +++ b/utilities_common/module.py @@ -134,3 +134,89 @@ def module_post_startup(self, module_name): return False return True + + def set_module_state_transition(self, module_name, flag_value): + """ + Set the state_transition_in_progress flag for the specified module. + + Args: + module_name (str): The name of the module. + flag_value (bool): The value to set for the state_transition_in_progress flag. + + Returns: + bool: True if the flag was successfully set, False otherwise. + """ + module_name = module_name.upper() + module_index = self.try_get_args(self.platform_chassis.get_module_index, module_name, + default=INVALID_MODULE_INDEX) + if module_index < 0: + log.log_error("Unable to get module-index for {}".format(module_name)) + return False + + if not hasattr(self.platform_chassis.get_module(module_index), 'set_module_state_transition'): + log.log_error("Set module state transition method not found in platform chassis") + return False + + log.log_info("Setting state_transition_in_progress flag for module {} to {}...".format(module_name, flag_value)) + # Map boolean flag_value to appropriate transition type + transition_type = "reboot" if flag_value else "shutdown" + status = self.try_get_args(self.platform_chassis.get_module(module_index).set_module_state_transition, + module_name, transition_type, default=False) + if not status: + log.log_error("Set module state transition flag status for module {}: {}".format(module_name, status)) + return False + + return True + + def clear_module_state_transition(self, module_name): + """ + Clear the state_transition_in_progress flag for the specified module. + + Args: + module_name (str): The name of the module. + Returns: + bool: True if the flag was successfully cleared, False otherwise. + """ + module_name = module_name.upper() + module_index = self.try_get_args(self.platform_chassis.get_module_index, module_name, + default=INVALID_MODULE_INDEX) + if module_index < 0: + log.log_error("Unable to get module-index for {}".format(module_name)) + return False + + if not hasattr(self.platform_chassis.get_module(module_index), 'clear_module_state_transition'): + log.log_error("Clear module state transition method not found in platform chassis") + return False + + log.log_info("Clearing state_transition_in_progress flag for module {}...".format(module_name)) + status = self.try_get_args(self.platform_chassis.get_module(module_index).clear_module_state_transition, + module_name, default=False) + if not status: + log.log_error("Clear module state transition flag status for module {}: {}".format(module_name, status)) + return False + + return True + + def get_module_state_transition(self, module_name): + """ + Get the state_transition_in_progress flag for the specified module. + + Args: + module_name (str): The name of the module. + Returns: + bool: The value of the state_transition_in_progress flag, or False if unable to retrieve it. + """ + module_name = module_name.upper() + module_index = self.try_get_args(self.platform_chassis.get_module_index, module_name, + default=INVALID_MODULE_INDEX) + if module_index < 0: + log.log_error("Unable to get module-index for {}".format(module_name)) + return False + + if not hasattr(self.platform_chassis.get_module(module_index), 'get_module_state_transition'): + log.log_error("Get module state transition method not found in platform chassis") + return False + + log.log_info("Getting state_transition_in_progress flag for module {}...".format(module_name)) + return self.try_get_args(self.platform_chassis.get_module(module_index).get_module_state_transition, + module_name, default=False)