diff --git a/changelogs/fragments/444-add-buffer-init-attribute.yaml b/changelogs/fragments/444-add-buffer-init-attribute.yaml new file mode 100644 index 000000000..6c2f1f30f --- /dev/null +++ b/changelogs/fragments/444-add-buffer-init-attribute.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_qos_buffer - Add 'buffer_init' attribute (https://github.com/ansible-collection/dellemc.enterprise_sonic/pull/444). diff --git a/playbooks/common_examples/qos_buffer_init.yaml b/playbooks/common_examples/qos_buffer_init.yaml deleted file mode 100644 index 4103121f4..000000000 --- a/playbooks/common_examples/qos_buffer_init.yaml +++ /dev/null @@ -1,50 +0,0 @@ ---- -- name: Initializing buffer - hosts: datacenter - connection: network_cli - become: yes - gather_facts: no - collections: - - dellemc.enterprise_sonic - tasks: - - name: Executing the buffer init command - ignore_errors: yes - sonic_config: - lines: - - command: buffer init lossless - prompt: '\[Proceed y/N\]:' - answer: 'y' - - name: print a debug message - ansible.builtin.debug: - msg: Made it to the debug message after issuing command with reboot -- name: Connecting to server after reboot - hosts: datacenter - connection: network_cli - become: yes - gather_facts: no - collections: - - dellemc.enterprise_sonic - tasks: - - name: "Wait for Connection" - ansible.builtin.wait_for_connection: - connect_timeout: 20 - sleep: 5 - delay: 200 - timeout: 400 - - name: print a debug message - ansible.builtin.debug: - msg: Made it to the final debug message after connection re-establishment - - name: Configuring QoS buffer - connection: network_cli - sonic_qos_buffer: - config: - buffer_pools: - - name: ingress_lossless_pool - xoff: 2500000 - buffer_profiles: - - name: profile1 - pool: ingress_lossless_pool - size: 40 - static_threshold: 20 - pause_threshold: 50000 - state: merged diff --git a/plugins/module_utils/network/sonic/argspec/qos_buffer/qos_buffer.py b/plugins/module_utils/network/sonic/argspec/qos_buffer/qos_buffer.py index e069d0480..255aa19da 100644 --- a/plugins/module_utils/network/sonic/argspec/qos_buffer/qos_buffer.py +++ b/plugins/module_utils/network/sonic/argspec/qos_buffer/qos_buffer.py @@ -39,6 +39,7 @@ def __init__(self, **kwargs): argument_spec = { 'config': { 'options': { + 'buffer_init': {'type': 'bool'}, 'buffer_pools': { 'elements': 'dict', 'options': { diff --git a/plugins/module_utils/network/sonic/config/qos_buffer/qos_buffer.py b/plugins/module_utils/network/sonic/config/qos_buffer/qos_buffer.py index a4ae9c6b7..bab5cf631 100644 --- a/plugins/module_utils/network/sonic/config/qos_buffer/qos_buffer.py +++ b/plugins/module_utils/network/sonic/config/qos_buffer/qos_buffer.py @@ -32,21 +32,21 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( to_request, - edit_config + edit_config_reboot ) from copy import deepcopy QOS_BUFFER_PATH = '/data/openconfig-qos:qos/openconfig-qos-buffer:buffer' -PATCH = 'patch' +BUFFER_INIT_PATH = '/operations/openconfig-qos-private:qos-buffer-config' DELETE = 'delete' TEST_KEYS = [ {'buffer_pools': {'name': ''}}, {'buffer_profiles': {'name': ''}} ] + TEST_KEYS_formatted_diff = [ - {'buffer_pools': {'name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, {'buffer_profiles': {'name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}} ] @@ -95,12 +95,11 @@ def execute_module(self): if commands and len(requests) > 0: if not self._module.check_mode: try: - edit_config(self._module, to_request(self._module, requests)) + edit_config_reboot(self._module, to_request(self._module, requests)) except ConnectionError as exc: - self._module.fail_json(msg=str(exc), code=exc.code) + pass result['changed'] = True result['commands'] = commands - changed_qos_buffer_facts = self.get_qos_buffer_facts() result['before'] = existing_qos_buffer_facts @@ -132,7 +131,7 @@ def set_config(self, existing_qos_buffer_facts): :returns: the commands necessary to migrate the current configuration to the desired configuration """ - want = self._module.params['config'] + want = remove_empties(self._module.params['config']) have = existing_qos_buffer_facts resp = self.set_state(want, have) return to_list(resp) @@ -186,7 +185,6 @@ def _state_deleted(self, want, have): of the provided objects """ is_delete_all = False - want = remove_empties(want) if not want: commands = deepcopy(have) @@ -203,11 +201,19 @@ def _state_deleted(self, want, have): return commands, requests def get_modify_qos_buffer_request(self, commands): - request = None + requests = [] buffer_dict = {} + buffer_init = commands.get('buffer_init') buffer_pools = commands.get('buffer_pools') buffer_profiles = commands.get('buffer_profiles') + if buffer_init is not None: + if buffer_init: + payload = {'openconfig-qos-private:input': {'operation': 'INIT'}} + else: + payload = {'openconfig-qos-private:input': {'operation': 'CLEAR'}} + requests.append({'path': BUFFER_INIT_PATH, 'method': 'post', 'data': payload}) + if buffer_pools: buffer_pool_list = [] for pool in buffer_pools: @@ -256,9 +262,9 @@ def get_modify_qos_buffer_request(self, commands): if buffer_dict: payload = {'openconfig-qos-buffer:buffer': buffer_dict} - request = {'path': QOS_BUFFER_PATH, 'method': PATCH, 'data': payload} + requests.append({'path': QOS_BUFFER_PATH, 'method': 'patch', 'data': payload}) - return request + return requests def get_delete_qos_buffer_requests(self, commands, have, is_delete_all): requests = [] diff --git a/plugins/module_utils/network/sonic/facts/qos_buffer/qos_buffer.py b/plugins/module_utils/network/sonic/facts/qos_buffer/qos_buffer.py index 565018bf2..e3bc25b56 100644 --- a/plugins/module_utils/network/sonic/facts/qos_buffer/qos_buffer.py +++ b/plugins/module_utils/network/sonic/facts/qos_buffer/qos_buffer.py @@ -13,7 +13,6 @@ __metaclass__ = type from copy import deepcopy - from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( utils, ) @@ -25,6 +24,7 @@ to_request, edit_config ) +from ansible.module_utils.connection import ConnectionError class Qos_bufferFacts(object): @@ -56,47 +56,43 @@ def populate_facts(self, connection, ansible_facts, data=None): objs = [] if not data: - cfg = self.get_config(self._module) - data = self.update_qos_buffer(cfg) - objs = self.render_config(self.generated_spec, data) + data = self.update_qos_buffer(self._module) + objs = data facts = {} if objs: - params = utils.validate_config(self.argument_spec, {'config': remove_empties(objs)}) - facts['qos_buffer'] = params['config'] + params = utils.validate_config(self.argument_spec, {'config': objs}) + facts['qos_buffer'] = remove_empties(params['config']) ansible_facts['ansible_network_resources'].update(facts) return ansible_facts - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values - - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - return conf - - def get_config(self, module): + def get_config(self, module, path, key_name): + """Retrieve configuration from device""" cfg = None - get_path = '/data/openconfig-qos:qos/openconfig-qos-buffer:buffer' - request = {'path': get_path, 'method': 'get'} + request = {'path': path, 'method': 'get'} try: response = edit_config(module, to_request(module, request)) - if 'openconfig-qos-buffer:buffer' in response[0][1]: - cfg = response[0][1].get('openconfig-qos-buffer:buffer') + if key_name in response[0][1]: + cfg = response[0][1].get(key_name) except ConnectionError as exc: module.fail_json(msg=str(exc), code=exc.code) return cfg - def update_qos_buffer(self, cfg): - + def update_qos_buffer(self, module): config_dict = {} - if cfg: - buffer_pools = cfg.get('buffer-pools') + + path = '/data/sonic-switch:sonic-switch/SWITCH/SWITCH_LIST=switch/buffer_mode_lossless' + sonic_cfg = self.get_config(module, path, 'sonic-switch:buffer_mode_lossless') + if sonic_cfg: + config_dict['buffer_init'] = sonic_cfg + else: + config_dict['buffer_init'] = False + + path = '/data/openconfig-qos:qos/openconfig-qos-buffer:buffer' + oc_cfg = self.get_config(module, path, 'openconfig-qos-buffer:buffer') + if oc_cfg: + buffer_pools = oc_cfg.get('buffer-pools') if buffer_pools: pool_list = buffer_pools.get('buffer-pool') if pool_list: @@ -116,7 +112,7 @@ def update_qos_buffer(self, cfg): if buffer_pool_list: config_dict['buffer_pools'] = buffer_pool_list - buffer_profiles = cfg.get('buffer-profiles') + buffer_profiles = oc_cfg.get('buffer-profiles') if buffer_profiles: profile_list = buffer_profiles.get('buffer-profile') if profile_list: diff --git a/plugins/modules/sonic_qos_buffer.py b/plugins/modules/sonic_qos_buffer.py index 41b261bf0..db2fb32fc 100644 --- a/plugins/modules/sonic_qos_buffer.py +++ b/plugins/modules/sonic_qos_buffer.py @@ -36,12 +36,6 @@ notes: - Tested against Enterprise SONiC Distribution by Dell Technologies. - Supports C(check_mode). -- The SONiC buffer initialization command must be executed before attempting to apply -- the configuration commands provided in this resource module. The buffer initialization will cause -- the switch to reboot. See the "playbooks/common_examples/QoS_buffer_init.yaml" file -- in this repo for an example playbook that initializes the buffers, waits for the reboot, -- then proceeds with execution of QoS buffer configuration commands. Alternatively, make use of the -- sonic_roce resource module to enable RoCEv2 default buffer configuration. short_description: Manage QoS buffer configuration on SONiC description: - This module provides configuration management of QoS buffer for devices running SONiC @@ -52,6 +46,11 @@ - QoS buffer configuration type: dict suboptions: + buffer_init: + description: + - Initialize QoS buffer based on system defaults + type: bool + version_added: 3.0.0 buffer_pools: description: - Buffer pools configuration @@ -134,6 +133,7 @@ - name: Merge QoS buffer configuration dellemc.enterprise_sonic.sonic_qos_buffer: config: + buffer_init: true buffer_pools: - name: ingress_lossless_pool xoff: 3500000 diff --git a/tests/regression/roles/sonic_qos_buffer/defaults/main.yml b/tests/regression/roles/sonic_qos_buffer/defaults/main.yml index 6276405ce..0d3d7c022 100644 --- a/tests/regression/roles/sonic_qos_buffer/defaults/main.yml +++ b/tests/regression/roles/sonic_qos_buffer/defaults/main.yml @@ -7,6 +7,7 @@ tests: description: Configure QoS buffer state: merged input: + buffer_init: true buffer_pools: - name: ingress_lossless_pool xoff: 2500000 @@ -64,3 +65,9 @@ tests: - name: profile1 - name: profile2 - name: profile3 + + - name: test_case_05 + description: Deinit buffer + state: merged + input: + buffer_init: false diff --git a/tests/regression/roles/sonic_qos_buffer/tasks/cleanup_tests.yaml b/tests/regression/roles/sonic_qos_buffer/tasks/cleanup_tests.yaml deleted file mode 100644 index 4a893f71c..000000000 --- a/tests/regression/roles/sonic_qos_buffer/tasks/cleanup_tests.yaml +++ /dev/null @@ -1,6 +0,0 @@ -- name: Disable RoCE - sonic_roce: - config: - roce_enable: False - state: merged - ignore_errors: yes diff --git a/tests/regression/roles/sonic_qos_buffer/tasks/main.yml b/tests/regression/roles/sonic_qos_buffer/tasks/main.yml index b54c27134..8fc8a8ab1 100644 --- a/tests/regression/roles/sonic_qos_buffer/tasks/main.yml +++ b/tests/regression/roles/sonic_qos_buffer/tasks/main.yml @@ -3,12 +3,6 @@ - set_fact: base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}" -- name: Preparation tests - include_tasks: preparation_tests.yaml - - name: "Test {{ module_name }} started ..." include_tasks: tasks_template.yaml loop: "{{ tests }}" - -- name: "Cleanup tests {{ module_name }} started" - include_tasks: cleanup_tests.yaml diff --git a/tests/regression/roles/sonic_qos_buffer/tasks/preparation_tests.yaml b/tests/regression/roles/sonic_qos_buffer/tasks/preparation_tests.yaml deleted file mode 100644 index 6b5101c7a..000000000 --- a/tests/regression/roles/sonic_qos_buffer/tasks/preparation_tests.yaml +++ /dev/null @@ -1,6 +0,0 @@ -- name: Enable RoCE - sonic_roce: - config: - roce_enable: True - state: merged - ignore_errors: yes diff --git a/tests/unit/modules/network/sonic/fixtures/sonic_qos_buffer.yaml b/tests/unit/modules/network/sonic/fixtures/sonic_qos_buffer.yaml index ce8f0af18..baf149eaf 100644 --- a/tests/unit/modules/network/sonic/fixtures/sonic_qos_buffer.yaml +++ b/tests/unit/modules/network/sonic/fixtures/sonic_qos_buffer.yaml @@ -2,6 +2,7 @@ merged_01: module_args: config: + buffer_init: true buffer_pools: - name: ingress_lossless_pool xoff: 2500000 @@ -16,12 +17,22 @@ merged_01: size: 75 dynamic_threshold: -1 existing_qos_buffer_config: - - path: "/data/openconfig-qos:qos/openconfig-qos-buffer:buffer" + - path: '/data/sonic-switch:sonic-switch/SWITCH/SWITCH_LIST=switch/buffer_mode_lossless' + response: + code: 200 + value: + sonic-switch:buffer_mode_lossless: false + - path: '/data/openconfig-qos:qos/openconfig-qos-buffer:buffer' response: code: 200 expected_config_requests: - - path: "/data/openconfig-qos:qos/openconfig-qos-buffer:buffer" - method: "patch" + - path: '/operations/openconfig-qos-private:qos-buffer-config' + method: 'post' + data: + openconfig-qos-private:input: + operation: 'INIT' + - path: '/data/openconfig-qos:qos/openconfig-qos-buffer:buffer' + method: 'patch' data: openconfig-qos-buffer:buffer: buffer-pools: @@ -46,6 +57,26 @@ merged_01: size: '75' dynamic-threshold: -1 +merged_02: + module_args: + config: + buffer_init: false + existing_qos_buffer_config: + - path: '/data/sonic-switch:sonic-switch/SWITCH/SWITCH_LIST=switch/buffer_mode_lossless' + response: + code: 200 + value: + sonic-switch:buffer_mode_lossless: true + - path: '/data/openconfig-qos:qos/openconfig-qos-buffer:buffer' + response: + code: 200 + expected_config_requests: + - path: '/operations/openconfig-qos-private:qos-buffer-config' + method: 'post' + data: + openconfig-qos-private:input: + operation: 'CLEAR' + deleted_01: module_args: config: @@ -58,7 +89,10 @@ deleted_01: - name: profile3 state: deleted existing_qos_buffer_config: - - path: "/data/openconfig-qos:qos/openconfig-qos-buffer:buffer" + - path: '/data/sonic-switch:sonic-switch/SWITCH/SWITCH_LIST=switch/buffer_mode_lossless' + response: + code: 200 + - path: '/data/openconfig-qos:qos/openconfig-qos-buffer:buffer' response: code: 200 value: @@ -88,15 +122,15 @@ deleted_01: size: '85' dynamic-threshold: 2 expected_config_requests: - - path: "/data/openconfig-qos:qos/openconfig-qos-buffer:buffer/buffer-profiles/buffer-profile=profile1/config/static-threshold" - method: "delete" + - path: '/data/openconfig-qos:qos/openconfig-qos-buffer:buffer/buffer-profiles/buffer-profile=profile1/config/static-threshold' + method: 'delete' data: - - path: "/data/openconfig-qos:qos/openconfig-qos-buffer:buffer/buffer-profiles/buffer-profile=profile1/config/pause-threshold" - method: "delete" + - path: '/data/openconfig-qos:qos/openconfig-qos-buffer:buffer/buffer-profiles/buffer-profile=profile1/config/pause-threshold' + method: 'delete' data: - - path: "/data/openconfig-qos:qos/openconfig-qos-buffer:buffer/buffer-profiles/buffer-profile=profile2/config/dynamic-threshold" - method: "delete" + - path: '/data/openconfig-qos:qos/openconfig-qos-buffer:buffer/buffer-profiles/buffer-profile=profile2/config/dynamic-threshold' + method: 'delete' data: - - path: "/data/openconfig-qos:qos/openconfig-qos-buffer:buffer/buffer-profiles/buffer-profile=profile3" - method: "delete" + - path: '/data/openconfig-qos:qos/openconfig-qos-buffer:buffer/buffer-profiles/buffer-profile=profile3' + method: 'delete' data: diff --git a/tests/unit/modules/network/sonic/test_sonic_qos_buffer.py b/tests/unit/modules/network/sonic/test_sonic_qos_buffer.py index f173b0e6d..77ed7580d 100644 --- a/tests/unit/modules/network/sonic/test_sonic_qos_buffer.py +++ b/tests/unit/modules/network/sonic/test_sonic_qos_buffer.py @@ -22,11 +22,8 @@ def setUpClass(cls): cls.mock_facts_edit_config = patch( "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.qos_buffer.qos_buffer.edit_config" ) - cls.mock_config_edit_config = patch( - "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.qos_buffer.qos_buffer.edit_config" - ) - cls.mock_utils_edit_config = patch( - "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.edit_config" + cls.mock_config_edit_config_reboot = patch( + "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.config.qos_buffer.qos_buffer.edit_config_reboot" ) cls.mock_get_interface_naming_mode = patch( "ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils.get_device_interface_naming_mode" @@ -36,20 +33,17 @@ def setUpClass(cls): def setUp(self): super(TestSonicQosBufferModule, self).setUp() self.facts_edit_config = self.mock_facts_edit_config.start() - self.config_edit_config = self.mock_config_edit_config.start() + self.config_edit_config = self.mock_config_edit_config_reboot.start() self.facts_edit_config.side_effect = self.facts_side_effect self.config_edit_config.side_effect = self.config_side_effect self.get_interface_naming_mode = self.mock_get_interface_naming_mode.start() self.get_interface_naming_mode.return_value = 'native' - self.utils_edit_config = self.mock_utils_edit_config.start() - self.utils_edit_config.side_effect = self.config_side_effect def tearDown(self): super(TestSonicQosBufferModule, self).tearDown() self.mock_facts_edit_config.stop() - self.mock_config_edit_config.stop() + self.mock_config_edit_config_reboot.stop() self.mock_get_interface_naming_mode.stop() - self.mock_utils_edit_config.stop() def test_sonic_qos_buffer_merged_01(self): set_module_args(self.fixture_data['merged_01']['module_args']) @@ -58,6 +52,13 @@ def test_sonic_qos_buffer_merged_01(self): result = self.execute_module(changed=True) self.validate_config_requests() + def test_sonic_qos_buffer_merged_02(self): + set_module_args(self.fixture_data['merged_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['merged_02']['existing_qos_buffer_config']) + self.initialize_config_requests(self.fixture_data['merged_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + def test_sonic_qos_buffer_deleted_01(self): set_module_args(self.fixture_data['deleted_01']['module_args']) self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_qos_buffer_config'])