diff --git a/config/main.py b/config/main.py index dbd574ff01..074169a6cf 100644 --- a/config/main.py +++ b/config/main.py @@ -799,6 +799,7 @@ def _wait_until_clear(tables, interval=0.5, timeout=30, verbose=False): def _clear_qos(delay=False, verbose=False): + status = True QOS_TABLE_NAMES = [ 'PORT_QOS_MAP', 'QUEUE', @@ -838,7 +839,8 @@ def _clear_qos(delay=False, verbose=False): device_metadata = config_db.get_entry('DEVICE_METADATA', 'localhost') # Traditional buffer manager do not remove buffer tables in any case, no need to wait. timeout = 120 if device_metadata and device_metadata.get('buffer_model') == 'dynamic' else 0 - _wait_until_clear(["BUFFER_*_TABLE:*", "BUFFER_*_SET"], interval=0.5, timeout=timeout, verbose=verbose) + status = _wait_until_clear(["BUFFER_*_TABLE:*", "BUFFER_*_SET"], interval=0.5, timeout=timeout, verbose=verbose) + return status def _get_sonic_generated_services(num_asic): if not os.path.isfile(SONIC_GENERATED_SERVICE_PATH): @@ -3161,6 +3163,7 @@ def _update_buffer_calculation_model(config_db, model): help="Dry run, writes config to the given file" ) def reload(ctx, no_dynamic_buffer, no_delay, dry_run, json_data, ports, verbose): + status = True """Reload QoS configuration""" if ports: log.log_info("'qos reload --ports {}' executing...".format(ports)) @@ -3169,7 +3172,7 @@ def reload(ctx, no_dynamic_buffer, no_delay, dry_run, json_data, ports, verbose) log.log_info("'qos reload' executing...") if not dry_run: - _clear_qos(delay = not no_delay, verbose=verbose) + status = _clear_qos(delay=not no_delay, verbose=verbose) _, hwsku_path = device_info.get_paths_to_platform_and_hwsku_dirs() sonic_version_file = device_info.get_sonic_version_file() @@ -3252,6 +3255,9 @@ def reload(ctx, no_dynamic_buffer, no_delay, dry_run, json_data, ports, verbose) if buffer_model_updated: print("Buffer calculation model updated, restarting swss is required to take effect") + if not status: + sys.exit(1) + def _qos_update_ports(ctx, ports, dry_run, json_data): """Reload QoS configuration""" _, hwsku_path = device_info.get_paths_to_platform_and_hwsku_dirs() diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index a1c869c79a..b5fdf4ef70 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -9636,6 +9636,7 @@ Some of the example QOS configurations that users can modify are given below. In this example, it uses the buffers.json.j2 file and qos.json.j2 file from platform specific folders. When there are no changes in the platform specific configutation files, they internally use the file "/usr/share/sonic/templates/buffers_config.j2" and "/usr/share/sonic/templates/qos_config.j2" to generate the configuration. + When an error occurs, such as "Operation not completed successfully, please save and reload configuration," the system will record the status, after executing all the latter commands, exit with code 1. ``` **config qos reload --ports port_list** diff --git a/tests/config_test.py b/tests/config_test.py index bd38011b5c..2d35ab7427 100644 --- a/tests/config_test.py +++ b/tests/config_test.py @@ -1607,6 +1607,46 @@ def test_qos_clear_no_wait(self, _wait_until_clear): _clear_qos(True, False) _wait_until_clear.assert_called_with(['BUFFER_*_TABLE:*', 'BUFFER_*_SET'], interval=0.5, timeout=0, verbose=False) + @mock.patch('config.main._wait_until_clear') + def test_clear_qos_without_delay(self, mock_wait_until_clear): + from config.main import _clear_qos + + status = _clear_qos(False, False) + mock_wait_until_clear.assert_not_called() + assert status is True + + @mock.patch('config.main._wait_until_clear') + def test_clear_qos_with_delay_returns_true(self, mock_wait_until_clear): + from config.main import _clear_qos + mock_wait_until_clear.return_value = True + + status = _clear_qos(True, False) + mock_wait_until_clear.assert_called_once() + assert status is True + + @mock.patch('config.main._wait_until_clear') + def test_clear_qos_with_delay_returns_false(self, mock_wait_until_clear): + from config.main import _clear_qos + mock_wait_until_clear.return_value = False + + status = _clear_qos(True, False) + mock_wait_until_clear.assert_called_once() + assert status is False + + @patch('config.main._wait_until_clear') + def test_qos_reload_not_empty_should_exit(self, mock_wait_until_clear): + mock_wait_until_clear.return_value = False + runner = CliRunner() + output_file = os.path.join(os.sep, "tmp", "qos_config_output.json") + print("Saving output in {}".format(output_file)) + result = runner.invoke( + config.config.commands["qos"], ["reload"] + ) + print(result.exit_code) + print(result.output) + # Expect sys.exit(1) when _wait_until_clear returns False + assert result.exit_code == 1 + def test_qos_reload_single( self, get_cmd_module, setup_qos_mock_apis, setup_single_broadcom_asic