diff --git a/changelogs/fragments/251-replaced-overridden-for-bgp-communities-module.yaml b/changelogs/fragments/251-replaced-overridden-for-bgp-communities-module.yaml new file mode 100644 index 000000000..5b9da775a --- /dev/null +++ b/changelogs/fragments/251-replaced-overridden-for-bgp-communities-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp_communities - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/251). diff --git a/changelogs/fragments/252-replaced-overridden-for-bgp-ext-communities-module.yaml b/changelogs/fragments/252-replaced-overridden-for-bgp-ext-communities-module.yaml new file mode 100644 index 000000000..5240a2069 --- /dev/null +++ b/changelogs/fragments/252-replaced-overridden-for-bgp-ext-communities-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_bgp_ext_communities - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/252). diff --git a/changelogs/fragments/279-playbook-check-diff-modes-and-radius-server-implement.yaml b/changelogs/fragments/279-playbook-check-diff-modes-and-radius-server-implement.yaml index 398708194..268cf24cc 100644 --- a/changelogs/fragments/279-playbook-check-diff-modes-and-radius-server-implement.yaml +++ b/changelogs/fragments/279-playbook-check-diff-modes-and-radius-server-implement.yaml @@ -1,2 +1,2 @@ -major_changes: - - Playbook check and diff modes utility functions and sonic_radius_server module support for the check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/279). +minor_changes: + - sonic_radius_server - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/279). diff --git a/changelogs/fragments/281-playbook-check-diff-modes-for-ntp-module.yaml b/changelogs/fragments/281-playbook-check-diff-modes-for-ntp-module.yaml index 360f4297e..ba49e0d93 100644 --- a/changelogs/fragments/281-playbook-check-diff-modes-for-ntp-module.yaml +++ b/changelogs/fragments/281-playbook-check-diff-modes-for-ntp-module.yaml @@ -1,2 +1,3 @@ -major_changes: - - ntp_and_tacacs - Playbook check and diff modes support for sonic_ntp and sonic_tacacs_server modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/281). +minor_changes: + - sonic_ntp - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/281). + - sonic_tacacs_server - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/281). diff --git a/changelogs/fragments/284-playbook-check-diff-modes-for-system-and-port-group-modules.yaml b/changelogs/fragments/284-playbook-check-diff-modes-for-system-and-port-group-modules.yaml new file mode 100644 index 000000000..4600c5a3b --- /dev/null +++ b/changelogs/fragments/284-playbook-check-diff-modes-for-system-and-port-group-modules.yaml @@ -0,0 +1,3 @@ +minor_changes: + - sonic_system - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/284). + - sonic_port_group - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/284). diff --git a/changelogs/fragments/285-playbook-check-diff-modes-for-vrfs-logging-ip-neighbor.yaml b/changelogs/fragments/285-playbook-check-diff-modes-for-vrfs-logging-ip-neighbor.yaml index 2bfc3f4f2..a2fea0251 100644 --- a/changelogs/fragments/285-playbook-check-diff-modes-for-vrfs-logging-ip-neighbor.yaml +++ b/changelogs/fragments/285-playbook-check-diff-modes-for-vrfs-logging-ip-neighbor.yaml @@ -1,2 +1,4 @@ -major_changes: - - vrfs_logging_ip_neighbor - Playbook check and diff modes supports for sonic_vrfs, sonic_logging and sonic_ip_neighbor modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/285). +minor_changes: + - sonic_vrfs - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/285). + - sonic_logging - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/285). + - sonic_ip_neighbor - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/285). diff --git a/changelogs/fragments/301-playbook-check-diff-modes-for-vlans-interfaces.yaml b/changelogs/fragments/301-playbook-check-diff-modes-for-vlans-interfaces.yaml new file mode 100644 index 000000000..a41227037 --- /dev/null +++ b/changelogs/fragments/301-playbook-check-diff-modes-for-vlans-interfaces.yaml @@ -0,0 +1,3 @@ +minor_changes: + - sonic_vlans - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/301). + - sonic_interfaces - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/301). diff --git a/changelogs/fragments/303-playbook-check-diff-modes-for-l2-lag-interfaces.yaml b/changelogs/fragments/303-playbook-check-diff-modes-for-l2-lag-interfaces.yaml index ad0b74192..5bd905824 100644 --- a/changelogs/fragments/303-playbook-check-diff-modes-for-l2-lag-interfaces.yaml +++ b/changelogs/fragments/303-playbook-check-diff-modes-for-l2-lag-interfaces.yaml @@ -1,2 +1,3 @@ -major_changes: - - l2_lag_interfaces - Playbook check and diff modes supports for sonic_l2_interfaces and sonic_lag_interfaces modules (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/303). +minor_changes: + - sonic_l2_interfaces - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/303). + - sonic_lag_interfaces - Add support for playbook check and diff modes (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/303). diff --git a/changelogs/fragments/310-change-deleted-design-for-interfaces-module.yaml b/changelogs/fragments/310-change-deleted-design-for-interfaces-module.yaml new file mode 100644 index 000000000..7d61c00bb --- /dev/null +++ b/changelogs/fragments/310-change-deleted-design-for-interfaces-module.yaml @@ -0,0 +1,2 @@ +minor_changes: + - sonic_interfaces - Change deleted design for interfaces module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/310). diff --git a/plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py b/plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py index 867e55204..c90fab8e9 100644 --- a/plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py +++ b/plugins/module_utils/network/sonic/argspec/bgp_communities/bgp_communities.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -54,6 +54,6 @@ def __init__(self, **kwargs): 'default': 'standard', 'type': 'str'}}, 'type': 'list'}, - 'state': {'choices': ['merged', 'deleted'], + 'state': {'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str'}} # pylint: disable=C0301 diff --git a/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py b/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py index aec0f364a..4cee6182b 100644 --- a/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py +++ b/plugins/module_utils/network/sonic/argspec/bgp_ext_communities/bgp_ext_communities.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -68,7 +68,7 @@ def __init__(self, **kwargs): 'type': 'list' }, 'state': { - 'choices': ['merged', 'deleted'], + 'choices': ['merged', 'deleted', 'replaced', 'overridden'], 'default': 'merged', 'type': 'str' } diff --git a/plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py b/plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py index 670fb26d3..82ed70a3f 100644 --- a/plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py +++ b/plugins/module_utils/network/sonic/config/bgp_communities/bgp_communities.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -62,6 +62,13 @@ class Bgp_communities(ConfigBase): 'bgp_communities', ] + standard_communities_map = { + 'no_peer': 'NOPEER', + 'no_export': 'NO_EXPORT', + 'no_advertise': 'NO_ADVERTISE', + 'local_as': 'NO_EXPORT_SUBCONFED' + } + def __init__(self, module): super(Bgp_communities, self).__init__(module) @@ -89,6 +96,7 @@ def execute_module(self): existing_bgp_communities_facts = self.get_bgp_communities_facts() commands, requests = self.set_config(existing_bgp_communities_facts) + if commands and len(requests) > 0: if not self._module.check_mode: try: @@ -116,6 +124,13 @@ def set_config(self, existing_bgp_communities_facts): to the desired configuration """ want = self._module.params['config'] + if want: + for conf in want: + if conf.get("match", None): + conf["match"] = conf["match"].upper() + if conf.get("members", {}) and conf['members'].get("regex", []): + conf['members']['regex'].sort() + have = existing_bgp_communities_facts resp = self.set_state(want, have) return to_list(resp) @@ -138,17 +153,16 @@ def set_state(self, want, have): # fp.write('comm: have: ' + str(have) + '\n') # fp.write('comm: diff: ' + str(diff) + '\n') if state == 'overridden': - commands, requests = self._state_overridden(want, have, diff) + commands, requests = self._state_overridden(want, have) elif state == 'deleted': - commands, requests = self._state_deleted(want, have, diff) + commands, requests = self._state_deleted(want, have) elif state == 'merged': commands, requests = self._state_merged(want, have, diff) elif state == 'replaced': - commands, requests = self._state_replaced(want, have, diff) + commands, requests = self._state_replaced(want, have) return commands, requests - @staticmethod - def _state_replaced(**kwargs): + def _state_replaced(self, want, have): """ The command generator when state is replaced :rtype: A list @@ -156,10 +170,13 @@ def _state_replaced(**kwargs): to the desired configuration """ commands = [] - return commands + requests = [] + + commands, requests = self.get_replaced_overridden_config(want, have, "replaced") - @staticmethod - def _state_overridden(**kwargs): + return commands, requests + + def _state_overridden(self, want, have): """ The command generator when state is overridden :rtype: A list @@ -167,7 +184,11 @@ def _state_overridden(**kwargs): to the desired configuration """ commands = [] - return commands + requests = [] + + commands, requests = self.get_replaced_overridden_config(want, have, "overridden") + + return commands, requests def _state_merged(self, want, have, diff): """ The command generator when state is merged @@ -177,7 +198,7 @@ def _state_merged(self, want, have, diff): the current configuration """ commands = diff - requests = self.get_modify_bgp_community_requests(commands, have) + requests = self.get_modify_bgp_community_requests(commands, have, "merged") if commands and len(requests) > 0: commands = update_states(commands, "merged") else: @@ -185,7 +206,7 @@ def _state_merged(self, want, have, diff): return commands, requests - def _state_deleted(self, want, have, diff): + def _state_deleted(self, want, have): """ The command generator when state is deleted :rtype: A list @@ -217,28 +238,18 @@ def _state_deleted(self, want, have, diff): return commands, requests - def get_delete_single_bgp_community_member_requests(self, name, type, members): + def get_delete_single_bgp_community_member_requests(self, name, members): requests = [] for member in members: url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:" url = url + "bgp-defined-sets/community-sets/community-set={name}/config/{members_param}" method = "DELETE" - memberstr = member - if type == 'expanded': - memberstr = 'REGEX:' + member - members_params = {'community-member': memberstr} + members_params = {'community-member': member} members_str = urlencode(members_params) request = {"path": url.format(name=name, members_param=members_str), "method": method} requests.append(request) return requests - def get_delete_all_members_bgp_community_requests(self, name): - url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:" - url = url + "bgp-defined-sets/community-sets/community-set={}/config/community-member" - method = "DELETE" - request = {"path": url.format(name), "method": method} - return request - def get_delete_single_bgp_community_requests(self, name): url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set={}" method = "DELETE" @@ -255,70 +266,90 @@ def get_delete_all_bgp_communities(self, commands): return requests def get_delete_bgp_communities(self, commands, have, is_delete_all): - # with open('/root/ansible_log.log', 'a+') as fp: - # fp.write('bgp_commmunities: delete requests ************** \n') requests = [] if is_delete_all: requests = self.get_delete_all_bgp_communities(commands) else: for cmd in commands: name = cmd['name'] - type = cmd['type'] - members = cmd['members'] - if members: - if members['regex']: - diff_members = [] - for item in have: - if item['name'] == name and item['members']: - for member_want in members['regex']: - if str(member_want) in item['members']['regex']: - diff_members.append(member_want) - if diff_members: - requests.extend(self.get_delete_single_bgp_community_member_requests(name, type, diff_members)) - else: - for item in have: - if item['name'] == name: - if item['members']: - requests.append(self.get_delete_all_members_bgp_community_requests(name)) - else: - for item in have: - if item['name'] == name: + members = cmd.get('members', None) + cmd_type = cmd['type'] + diff_members = [] + + for item in have: + if item['name'] == name: + if 'permit' not in cmd or cmd['permit'] is None: + cmd['permit'] = item['permit'] + + if cmd == item: requests.append(self.get_delete_single_bgp_community_requests(name)) + break + + if cmd_type == "standard": + for attr in self.standard_communities_map: + if cmd.get(attr, None) and item[attr] and cmd[attr] == item[attr]: + diff_members.append(self.standard_communities_map[attr]) + + if members: + if members.get('regex', []): + for member_want in members['regex']: + if item.get('members', None) and item['members'].get('regex', []): + if str(member_want) in item['members']['regex']: + diff_members.append("REGEX:" + str(member_want)) + else: + requests.append(self.get_delete_single_bgp_community_requests(name)) + + else: + if cmd_type == "standard": + no_attr = True + for attr in self.standard_communities_map: + if cmd.get(attr, None): + no_attr = False + break + if no_attr: + requests.append(self.get_delete_single_bgp_community_requests(name)) + else: + requests.append(self.get_delete_single_bgp_community_requests(name)) + break + + if diff_members: + requests.extend(self.get_delete_single_bgp_community_member_requests(name, diff_members)) - # with open('/root/ansible_log.log', 'a+') as fp: - # fp.write('bgp_commmunities: delete requests' + str(requests) + '\n') return requests def get_new_add_request(self, conf): url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" method = "PATCH" - # members = conf['members'] - # members_str = ', '.join(members) - # members_list = list() - # for member in members.split(','): - # members_list.append(str(member)) + community_members = [] + community_action = "" if 'match' not in conf: conf['match'] = "ANY" - # with open('/root/ansible_log.log', 'a+') as fp: - # fp.write('bgp_communities: conf' + str(conf) + '\n') - if 'local_as' in conf and conf['local_as']: - conf['members']['regex'].append("NO_EXPORT_SUBCONFED") - if 'no_peer' in conf and conf['no_peer']: - conf['members']['regex'].append("NOPEER") - if 'no_export' in conf and conf['no_export']: - conf['members']['regex'].append("NO_EXPORT") - if 'no_advertise' in conf and conf['no_advertise']: - conf['members']['regex'].append("NO_ADVERTISE") - input_data = {'name': conf['name'], 'members_list': conf['members']['regex'], 'match': conf['match']} - if conf['type'] == 'expanded': - input_data['regex'] = "REGEX:" - else: - input_data['regex'] = "" + + if conf['type'] == 'standard': + for attr in self.standard_communities_map: + if attr in conf and conf[attr]: + community_members.append(self.standard_communities_map[attr]) + if 'members' in conf and conf['members'] and conf['members'].get('regex', []): + for i in conf['members']['regex']: + community_members.extend([str(i)]) + if not community_members: + self._module.fail_json(msg='Cannot create standard community-list {0} without community attributes'.format(conf['name'])) + + elif conf['type'] == 'expanded': + if 'members' in conf and conf['members'] and conf['members'].get('regex', []): + for i in conf['members']['regex']: + community_members.extend(["REGEX:" + str(i)]) + if not community_members: + self._module.fail_json(msg='Cannot create expanded community-list {0} without community attributes'.format(conf['name'])) + if conf['permit']: - input_data['permit'] = "PERMIT" + community_action = "PERMIT" else: - input_data['permit'] = "DENY" + community_action = "DENY" + + input_data = {'name': conf['name'], 'members_list': community_members, 'match': conf['match'].upper(), 'permit': community_action} + payload_template = """ { "openconfig-bgp-policy:community-sets": { @@ -328,7 +359,7 @@ def get_new_add_request(self, conf): "config": { "community-set-name": "{{name}}", "community-member": [ - {% for member in members_list %}"{{regex}}{{member}}"{%- if not loop.last -%},{% endif %}{%endfor%} + {% for member in members_list %}"{{member}}"{%- if not loop.last -%},{% endif %}{%endfor%} ], "openconfig-bgp-policy-ext:action": "{{permit}}", "match-set-options": "{{match}}" @@ -342,27 +373,118 @@ def get_new_add_request(self, conf): intended_payload = t.render(input_data) ret_payload = json.loads(intended_payload) request = {"path": url, "method": method, "data": ret_payload} - # with open('/root/ansible_log.log', 'a+') as fp: - # fp.write('bgp_communities: request' + str(request) + '\n') + return request - def get_modify_bgp_community_requests(self, commands, have): + def get_modify_bgp_community_requests(self, commands, have, cur_state): requests = [] if not commands: return requests for conf in commands: - for item in have: - if item['name'] == conf['name']: - if 'type' not in conf: - conf['type'] = item['type'] - if 'permit' not in conf: - conf['permit'] = item['permit'] - if 'match' not in conf: - conf['match'] = item['match'] - if 'members' not in conf: - conf['members'] = item['members'] + if cur_state == "merged": + for item in have: + if item['name'] == conf['name']: + if 'type' not in conf: + conf['type'] = item['type'] + if 'permit' not in conf or conf['permit'] is None: + conf['permit'] = item['permit'] + if 'match' not in conf: + conf['match'] = item['match'] + if conf['type'] == "standard": + for attr in self.standard_communities_map: + if attr not in conf and attr in item: + conf[attr] = item[attr] + else: + if 'members' not in conf: + if item.get('members', {}) and item['members'].get('regex', []): + conf['members'] = {'regex': item['members']['regex']} + else: + conf['members'] = item['members'] + break + new_req = self.get_new_add_request(conf) if new_req: requests.append(new_req) return requests + + def get_replaced_overridden_config(self, want, have, cur_state): + commands, requests = [], [] + + commands_del, requests_del = [], [] + commands_add, requests_add = [], [] + + for conf in want: + name = conf['name'] + in_have = False + for have_conf in have: + if have_conf['name'] == name: + in_have = True + if have_conf['type'] != conf['type']: + # If both community list are of same name but different types + commands_del.append(have_conf) + commands_add.append(conf) + else: + is_change = False + + if have_conf['permit'] != conf['permit']: + is_change = True + + if have_conf['match'] != conf['match']: + is_change = is_delete = True + + if conf["type"] == "standard": + no_attr = True + for attr in self.standard_communities_map: + if not conf.get(attr, None): + if have_conf.get(attr, None): + is_change = True + else: + no_attr = False + if not have_conf.get(attr, None): + is_change = True + + if no_attr: + # Since standard type needs atleast one attribute to exist + self._module.fail_json(msg='Cannot create standard community-list {0} without community attributes'.format(conf['name'])) + else: + members = conf.get('members', {}) + if members and members.get('regex', []): + if have_conf.get('members', {}) and have_conf['members'].get('regex', []): + if set(have_conf['members']['regex']).symmetric_difference(set(members['regex'])): + is_change = True + else: + # If there are no members in any community list of want, then + # that particular community list request to be ignored since + # expanded type needs community-member to exist + self._module.fail_json(msg='Cannot create expanded community-list {0} without community attributes'.format(conf['name'])) + + if is_change: + commands_add.append(conf) + commands_del.append(have_conf) + break + + if not in_have: + commands_add.append(conf) + + if cur_state == "overridden": + for have_conf in have: + in_want = next((conf for conf in want if conf['name'] == have_conf['name']), None) + if not in_want: + commands_del.append(have_conf) + + if commands_del: + requests_del = self.get_delete_bgp_communities(commands_del, have, False) + + if len(requests_del) > 0: + commands.extend(update_states(commands_del, "deleted")) + requests.extend(requests_del) + + if commands_add: + requests_add = self.get_modify_bgp_community_requests(commands_add, have, cur_state) + + if len(requests_add) > 0: + commands.extend(update_states(commands_add, cur_state)) + requests.extend(requests_add) + + return commands, requests diff --git a/plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py b/plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py index 751f88e48..8cd9953e6 100644 --- a/plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py +++ b/plugins/module_utils/network/sonic/config/bgp_ext_communities/bgp_ext_communities.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -60,6 +60,11 @@ class Bgp_ext_communities(ConfigBase): 'bgp_ext_communities', ] + standard_communities_map = { + "route_origin": "route-origin", + "route_target": "route-target" + } + def __init__(self, module): super(Bgp_ext_communities, self).__init__(module) @@ -87,6 +92,7 @@ def execute_module(self): existing_bgp_ext_communities_facts = self.get_bgp_ext_communities_facts() commands, requests = self.set_config(existing_bgp_ext_communities_facts) + if commands and len(requests) > 0: if not self._module.check_mode: try: @@ -114,6 +120,21 @@ def set_config(self, existing_bgp_ext_communities_facts): to the desired configuration """ want = self._module.params['config'] + if want: + for conf in want: + cmd_type = conf.get("type", None) + if cmd_type and conf.get("match", None): + conf['match'] = conf['match'].lower() + if cmd_type and conf.get("members", {}): + if cmd_type == "expanded": + if conf['members'].get("regex", []): + conf['members']['regex'].sort() + else: + if conf['members'].get("route_origin", []): + conf['members']['route_origin'].sort() + if conf['members'].get("route_target", []): + conf['members']['route_target'].sort() + have = existing_bgp_ext_communities_facts resp = self.set_state(want, have) return to_list(resp) @@ -133,17 +154,16 @@ def set_state(self, want, have): new_want = self.validate_type(want) diff = get_diff(new_want, have) if state == 'overridden': - commands, requests = self._state_overridden(want, have, diff) + commands, requests = self._state_overridden(want, have) elif state == 'deleted': - commands, requests = self._state_deleted(want, have, diff) + commands, requests = self._state_deleted(want, have) elif state == 'merged': commands, requests = self._state_merged(want, have, diff) elif state == 'replaced': - commands, requests = self._state_replaced(want, have, diff) + commands, requests = self._state_replaced(want, have) return commands, requests - @staticmethod - def _state_replaced(**kwargs): + def _state_replaced(self, want, have): """ The command generator when state is replaced :rtype: A list @@ -151,10 +171,13 @@ def _state_replaced(**kwargs): to the desired configuration """ commands = [] - return commands + requests = [] + + commands, requests = self.get_replaced_overridden_config(want, have, "replaced") - @staticmethod - def _state_overridden(**kwargs): + return commands, requests + + def _state_overridden(self, want, have): """ The command generator when state is overridden :rtype: A list @@ -162,7 +185,11 @@ def _state_overridden(**kwargs): to the desired configuration """ commands = [] - return commands + requests = [] + + commands, requests = self.get_replaced_overridden_config(want, have, "overridden") + + return commands, requests def _state_merged(self, want, have, diff): """ The command generator when state is merged @@ -172,7 +199,7 @@ def _state_merged(self, want, have, diff): the current configuration """ commands = diff - requests = self.get_modify_bgp_ext_community_requests(commands, have) + requests = self.get_modify_bgp_ext_community_requests(commands, have, "merged") if commands and len(requests) > 0: commands = update_states(commands, "merged") else: @@ -180,7 +207,7 @@ def _state_merged(self, want, have, diff): return commands, requests - def _state_deleted(self, want, have, diff): + def _state_deleted(self, want, have): """ The command generator when state is deleted :rtype: A list @@ -204,7 +231,7 @@ def _state_deleted(self, want, have, diff): return commands, requests - def get_delete_single_bgp_ext_community_member_requests(self, name, type, members): + def get_delete_single_bgp_ext_community_member_requests(self, name, members): requests = [] for member in members: url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:" @@ -216,13 +243,6 @@ def get_delete_single_bgp_ext_community_member_requests(self, name, type, member requests.append(request) return requests - def get_delete_all_members_bgp_ext_community_requests(self, name): - url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:" - url = url + "bgp-defined-sets/ext-community-sets/ext-community-set={}/config/ext-community-member" - method = "DELETE" - request = {"path": url.format(name), "method": method} - return request - def get_delete_single_bgp_ext_community_requests(self, name): url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set={}" method = "DELETE" @@ -245,71 +265,79 @@ def get_delete_bgp_ext_communities(self, commands, have, is_delete_all): else: for cmd in commands: name = cmd['name'] - type = cmd['type'] - members = cmd['members'] - if members: - if members['regex'] or members['route_origin'] or members['route_target']: - diff_members = [] - for item in have: - if item['name'] == name and item['members']: - if members['regex']: + cmd_type = cmd['type'] + members = cmd.get('members', None) + diff_members = [] + + for item in have: + if item["name"] == name: + if 'permit' not in cmd or cmd['permit'] is None: + cmd['permit'] = item['permit'] + if cmd == item: + requests.append(self.get_delete_single_bgp_ext_community_requests(name)) + break + + if members: + if cmd_type == "expanded": + if members.get('regex', []): for member_want in members['regex']: - if str(member_want) in item['members']['regex']: - diff_members.append('REGEX:' + str(member_want)) - if members['route_origin']: - for member_want in members['route_origin']: - if str(member_want) in item['members']['route_origin']: - diff_members.append("route-origin:" + str(member_want)) - if members['route_target']: - for member_want in members['route_target']: - if str(member_want) in item['members']['route_target']: - diff_members.append("route-target:" + str(member_want)) - if diff_members: - requests.extend(self.get_delete_single_bgp_ext_community_member_requests(name, type, diff_members)) - else: - for item in have: - if item['name'] == name: - if item['members']: - requests.append(self.get_delete_all_members_bgp_ext_community_requests(name)) - else: - for item in have: - if item['name'] == name: + if item.get("members", None) and item['members'].get('regex', []): + if str(member_want) in item['members']['regex']: + diff_members.append("REGEX:" + str(member_want)) + else: + requests.append(self.get_delete_single_bgp_ext_community_requests(name)) + else: + no_members = True + for attr in self.standard_communities_map: + if members.get(attr, []): + no_members = False + for member_want in members[attr]: + if item.get("members", None) and item['members'].get(attr, []): + if str(member_want) in item['members'][attr]: + diff_members.append(self.standard_communities_map[attr] + ":" + str(member_want)) + if no_members: + requests.append(self.get_delete_single_bgp_ext_community_requests(name)) + else: requests.append(self.get_delete_single_bgp_ext_community_requests(name)) + break + + if diff_members: + requests.extend(self.get_delete_single_bgp_ext_community_member_requests(name, diff_members)) + return requests def get_new_add_request(self, conf): url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" method = "PATCH" - members = conf.get('members', None) + community_members = [] + community_action = "" + if 'match' not in conf: conf['match'] = "ANY" - else: - conf['match'] = conf['match'].upper() - input_data = {'name': conf['name'], 'match': conf['match']} - - input_data['members_list'] = list() - if members: - regex = members.get('regex', None) - if regex: - input_data['members_list'].extend(["REGEX:" + cfg for cfg in regex]) - else: - route_target = members.get('route_target', None) - if route_target: - input_data['members_list'].extend(["route-target:" + cfg for cfg in route_target]) - route_origin = members.get('route_origin', None) - if route_origin: - input_data['members_list'].extend(["route-origin:" + cfg for cfg in route_origin]) if conf['type'] == 'expanded': - input_data['regex'] = "REGEX:" - else: - input_data['regex'] = "" + if 'members' in conf and conf['members'] and conf['members'].get('regex', []): + for i in conf['members']['regex']: + community_members.extend(["REGEX:" + str(i)]) + elif conf['type'] == 'standard': + for attr in self.standard_communities_map: + if 'members' in conf and conf['members'] and conf['members'].get(attr, []): + for i in conf['members'][attr]: + community_members.extend([self.standard_communities_map[attr] + ":" + str(i)]) + + if not community_members: + self._module.fail_json(msg='Cannot create {0} community-list {1} without community attributes'.format(conf['type'], conf['name'])) + return {} + if conf['permit']: - input_data['permit'] = "PERMIT" + community_action = "PERMIT" else: - input_data['permit'] = "DENY" + community_action = "DENY" + + input_data = {'name': conf['name'], 'members_list': community_members, 'match': conf['match'].upper(), 'permit': community_action} + payload_template = """ { "openconfig-bgp-policy:ext-community-sets": { @@ -335,23 +363,37 @@ def get_new_add_request(self, conf): request = {"path": url, "method": method, "data": ret_payload} return request - def get_modify_bgp_ext_community_requests(self, commands, have): + def get_modify_bgp_ext_community_requests(self, commands, have, cur_state): requests = [] if not commands: return requests for conf in commands: - for item in have: - if item['name'] == conf['name']: - if 'type' not in conf: - conf['type'] = item['type'] - if 'permit' not in conf: - conf['permit'] = item['permit'] - if 'match' not in conf: - conf['match'] = item['match'] - if 'members' not in conf: - conf['members'] = item['members'] - break + if cur_state == "merged": + for item in have: + if item['name'] == conf['name']: + if 'type' not in conf: + conf['type'] = item['type'] + if 'permit' not in conf or conf['permit'] is None: + conf['permit'] = item['permit'] + if 'match' not in conf: + conf['match'] = item['match'] + if 'members' not in conf: + if conf['type'] == "expanded": + if item.get('members', {}) and item['members'].get('regex', []): + conf['members'] = {'regex': item['members']['regex']} + else: + conf['members'] = item['members'] + else: + no_members = True + for attr in self.standard_communities_map: + if item.get('members', {}) and item['members'].get(attr, []): + no_members = False + conf['members'] = {attr: item['members'][attr]} + if no_members: + conf['members'] = item['members'] + break + new_req = self.get_new_add_request(conf) if new_req: requests.append(new_req) @@ -369,3 +411,84 @@ def validate_type(self, want): new_want.append(cfg) return new_want + + def get_replaced_overridden_config(self, want, have, cur_state): + commands, requests = [], [] + + commands_del, requests_del = [], [] + commands_add, requests_add = [], [] + + for conf in want: + name = conf['name'] + in_have = False + for have_conf in have: + if have_conf['name'] == name: + in_have = True + if have_conf['type'] != conf['type']: + # If both extended community list are of same name but different types + commands_del.append(have_conf) + commands_add.append(conf) + else: + is_change = False + + if have_conf['permit'] != conf['permit']: + is_change = True + + if have_conf['match'] != conf['match']: + is_change = True + + if conf["type"] == "expanded": + members = conf.get('members', {}) + if members and conf['members'].get('regex', []): + if have_conf.get('members', {}) and have_conf['members'].get('regex', []): + if set(have_conf['members']['regex']).symmetric_difference(set(members['regex'])): + is_change = True + else: + # If there are no members in any expanded ext community list of want, then + # abort the playbook with an error message explaining why the specified command is not valid + self._module.fail_json(msg='Cannot create expanded extended community-list ' + '{0} without community attributes'.format(conf['name'])) + else: + members = conf.get('members', {}) + no_members = True + for attr in self.standard_communities_map: + if members and conf['members'].get(attr, []): + no_members = False + if have_conf.get('members', {}) and have_conf['members'].get(attr, []): + if set(have_conf['members'][attr]).symmetric_difference(set(members[attr])): + is_change = True + + if no_members: + # If there are no members in any standard ext community list of want, then + # abort the playbook with an error message explaining why the specified command is not valid + self._module.fail_json(msg='Cannot create standard extended community-list ' + '{0} without community attributes'.format(conf['name'])) + + if is_change: + commands_add.append(conf) + commands_del.append(have_conf) + break + if not in_have: + commands_add.append(conf) + + if cur_state == "overridden": + for have_conf in have: + in_want = next((conf for conf in want if conf['name'] == have_conf['name']), None) + if not in_want: + commands_del.append(have_conf) + + if commands_del: + requests_del = self.get_delete_bgp_ext_communities(commands_del, have, False) + + if len(requests_del) > 0: + commands.extend(update_states(commands_del, "deleted")) + requests.extend(requests_del) + + if commands_add: + requests_add = self.get_modify_bgp_ext_community_requests(commands_add, have, cur_state) + + if len(requests_add) > 0: + commands.extend(update_states(commands_add, cur_state)) + requests.extend(requests_add) + + return commands, requests diff --git a/plugins/module_utils/network/sonic/config/interfaces/interfaces.py b/plugins/module_utils/network/sonic/config/interfaces/interfaces.py index 9fcb516e9..5b8ac564c 100644 --- a/plugins/module_utils/network/sonic/config/interfaces/interfaces.py +++ b/plugins/module_utils/network/sonic/config/interfaces/interfaces.py @@ -18,6 +18,15 @@ except ImportError: from urllib.parse import quote +""" +The use of natsort causes sanity error due to it is not available in python version currently used. +When natsort becomes available, the code here and below using it will be applied. +from natsort import ( + natsorted, + ns +) +""" +from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -37,12 +46,22 @@ from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( get_diff, update_states, - normalize_interface_name + normalize_interface_name, + remove_empties_from_list +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff ) from ansible.module_utils._text import to_native from ansible.module_utils.connection import ConnectionError import traceback +TEST_KEYS_formatted_diff = [ + {'config': {'name': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, +] + LIB_IMP_ERR = None ERR_MSG = None try: @@ -58,6 +77,24 @@ DELETE = 'delete' url = 'data/openconfig-interfaces:interfaces/interface=%s' +intf_speed_map = { + 0: 'SPEED_DEFAULT', + 10: "SPEED_10MB", + 100: "SPEED_100MB", + 1000: "SPEED_1GB", + 2500: "SPEED_2500MB", + 5000: "SPEED_5GB", + 10000: "SPEED_10GB", + 20000: "SPEED_20GB", + 25000: "SPEED_25GB", + 40000: "SPEED_40GB", + 50000: "SPEED_50GB", + 100000: "SPEED_100GB", + 200000: "SPEED_200GB", + 400000: "SPEED_400GB", + 800000: "SPEED_800GB" +} + class Interfaces(ConfigBase): """ @@ -118,6 +155,22 @@ def execute_module(self): if result['changed']: result['after'] = changed_interfaces_facts + new_config = changed_interfaces_facts + old_config = existing_interfaces_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_interfaces_facts, + TEST_KEYS_formatted_diff) + # See the above comment about natsort module + # new_config = natsorted(new_config, key=lambda x: x['name']) + # For time-being, use simple "sort" + new_config.sort(key=lambda x: x['name']) + result['after(generated)'] = new_config + old_config.sort(key=lambda x: x['name']) + + if self._module._diff: + result['config_diff'] = get_formatted_config_diff(old_config, + new_config) result['warnings'] = warnings return result @@ -130,9 +183,10 @@ def set_config(self, existing_interfaces_facts): to the desired configuration """ want = self._module.params['config'] - normalize_interface_name(want, self._module) have = existing_interfaces_facts + self.filter_out_mgmt_interface(want, have) + normalize_interface_name(want, self._module) resp = self.set_state(want, have) return to_list(resp) @@ -173,10 +227,19 @@ def _state_replaced(self, want, have, diff): to the desired configuration """ commands = self.filter_comands_to_change(diff, have) - requests = self.get_delete_interface_requests(commands, have) - requests.extend(self.get_modify_interface_requests(commands, have)) + commands_del = self.filter_comands_to_delete(commands, have) + requests = self.get_delete_interface_requests(commands_del, have) + commands_mer = self.filter_comands_to_change(commands, have) + requests.extend(self.get_modify_interface_requests(commands_mer, have)) if commands and len(requests) > 0: - commands = update_states(commands, "replaced") + commands_dlt, commands_rep = self.classify_delete_commands(commands_del) + commands = [] + if commands_dlt: + commands.extend(update_states(commands_dlt, "deleted")) + if commands_rep: + commands.extend(update_states(commands_rep, "replaced")) + if commands_mer: + commands.extend(update_states(commands_mer, "replaced")) else: commands = [] @@ -192,14 +255,18 @@ def _state_overridden(self, want, have, diff): to the desired configuration """ commands = [] - commands_del = self.filter_comands_to_change(want, have) + commands_chg = self.filter_comands_to_change(want, have) + commands_del = self.filter_comands_to_delete(commands_chg, have) requests = self.get_delete_interface_requests(commands_del, have) del_req_count = len(requests) if commands_del and del_req_count > 0: - commands_del = update_states(commands_del, "deleted") - commands.extend(commands_del) + commands_dlt, commands_ovr = self.classify_delete_commands(commands_del) + if commands_dlt: + commands.extend(update_states(commands_dlt, "deleted")) + if commands_ovr: + commands.extend(update_states(commands_ovr, "overridden")) - commands_over = diff + commands_over = self.filter_comands_to_change(diff, have) requests.extend(self.get_modify_interface_requests(commands_over, have)) if commands_over and len(requests) > del_req_count: commands_over = update_states(commands_over, "overridden") @@ -216,7 +283,7 @@ def _state_merged(self, want, have, diff): :returns: the commands necessary to merge the provided into the current configuration """ - commands = diff + commands = self.filter_comands_to_change(diff, have) requests = self.get_modify_interface_requests(commands, have) if commands and len(requests) > 0: commands = update_states(commands, "merged") @@ -236,34 +303,256 @@ def _state_deleted(self, want, have, diff): of the provided objects """ # if want is none, then delete all the interfaces + + want = remove_empties_from_list(want) + delete_all = False if not want: commands = have + delete_all = True else: commands = want - requests = self.get_delete_interface_requests(commands, have) - - if commands and len(requests) > 0: - commands = update_states(commands, "deleted") - else: - commands = [] + commands_del, commands_mer, requests = self.handle_delete_interface_config(commands, + have, + delete_all) + commands = [] + if commands_del: + commands.extend(update_states(commands_del, "deleted")) + if commands_mer: + commands.extend(update_states(commands_mer, "merged")) return commands, requests + def handle_delete_interface_config(self, commands, have, delete_all=False): + requests = [] + del_commands = [] + mer_commands = [] + if not commands: + return del_commands, mer_commands, requests + + # Create URL and payload + for cmd in commands: + name = cmd['name'] + have_conf = next((cfg for cfg in have if cfg['name'] == name), None) + if have_conf: + lp_key_set = set(cmd.keys()) + if name.startswith('Loopback'): + if delete_all or len(lp_key_set) == 1: + method = DELETE + lpbk_url = url % quote(name, safe='') + request = {"path": lpbk_url, "method": method} + requests.append(request) + + del_commands.append({'name': name}) + + continue + + if len(lp_key_set) == 1: + conf = deepcopy(have_conf) + else: + conf = deepcopy(cmd) + + new_mer_cmd = False + + request = self.build_delete_description_request(conf, have_conf) + if request: + requests.append(request) + new_mer_cmd = True + + request = self.build_delete_enabled_request(conf, have_conf) + if request: + requests.append(request) + new_mer_cmd = True + + request = self.build_delete_mtu_request(conf, have_conf) + if request: + requests.append(request) + new_mer_cmd = True + + request = self.build_delete_fec_request(conf, have_conf) + if request: + requests.append(request) + new_mer_cmd = True + + request = self.build_delete_speed_request(conf, have_conf) + if request: + requests.append(request) + new_mer_cmd = True + + request = self.build_delete_autoneg_request(conf, have_conf) + if request: + requests.append(request) + new_mer_cmd = True + + request = self.build_delete_advertised_speed_request(conf, have_conf) + if request: + requests.append(request) + new_mer_cmd = True + + if new_mer_cmd: + mer_commands.append(conf) + + return del_commands, mer_commands, requests + + def build_delete_description_request(self, conf, have_conf): + intf_name = conf['name'] + request = dict() + method = DELETE + + c_des = conf.get('description', None) + h_des = have_conf.get('description', None) + if c_des and h_des and h_des != '': + config_url = (url + '/config/description') % quote(intf_name, safe='') + request = {"path": config_url, "method": method} + + conf['description'] = '' + + return request + + def build_delete_enabled_request(self, conf, have_conf): + intf_name = conf['name'] + request = dict() + method = DELETE + + c_ena = conf.get('enabled', None) + h_ena = have_conf.get('enabled', None) + if c_ena is not None and h_ena is not None and h_ena: + config_url = (url + '/config/enabled') % quote(intf_name, safe='') + request = {"path": config_url, "method": method} + + conf['enabled'] = False + + return request + + def build_delete_mtu_request(self, conf, have_conf): + intf_name = conf['name'] + request = dict() + method = DELETE + + if not intf_name.startswith('Loopback'): + c_mtu = conf.get('mtu', None) + h_mtu = have_conf.get('mtu', None) + if c_mtu and h_mtu and h_mtu != 9100: + config_url = (url + '/config/mtu') % quote(intf_name, safe='') + request = {"path": config_url, "method": method} + + conf['mtu'] = 9100 + + return request + + def build_delete_fec_request(self, conf, have_conf): + intf_name = conf['name'] + request = dict() + method = PATCH + + if intf_name.startswith('Eth'): + c_fec = conf.get('fec', None) + h_fec = have_conf.get('fec', None) + if c_fec and h_fec and h_fec != 'FEC_DISABLED': + fec_url = '/openconfig-if-ethernet-ext2:port-fec' + eth_url = '/openconfig-if-ethernet:ethernet/config' + config_url = (url + eth_url + fec_url) % quote(intf_name, safe='') + payload = {'openconfig-if-ethernet-ext2:port-fec': 'FEC_DISABLED'} + request = {"path": config_url, "method": method, 'data': payload} + + conf['fec'] = 'FEC_DISABLED' + + return request + + def build_delete_speed_request(self, conf, have_conf): + intf_name = conf['name'] + request = dict() + method = DELETE + + if intf_name.startswith('Eth'): + c_spd = conf.get('speed', None) + h_spd = have_conf.get('speed', None) + if c_spd and h_spd: + dft_spd = self.retrieve_default_intf_speed(intf_name) + if h_spd != dft_spd: + spd_url = '/openconfig-if-ethernet:ethernet/config/port-speed' + config_url = (url + spd_url) % quote(intf_name, safe='') + request = {"path": config_url, "method": method} + + conf['speed'] = dft_spd + + return request + + def build_delete_autoneg_request(self, conf, have_conf): + intf_name = conf['name'] + request = dict() + method = DELETE + + if intf_name.startswith('Eth'): + c_ang = conf.get('auto_negotiate', None) + h_ang = have_conf.get('auto_negotiate', None) + if c_ang is not None and h_ang is not None and h_ang: + ang_url = '/auto-negotiate' + eth_url = '/openconfig-if-ethernet:ethernet/config' + config_url = (url + eth_url + ang_url) % quote(intf_name, safe='') + request = {"path": config_url, "method": method} + + conf['auto_negotiate'] = False + + return request + + def build_delete_advertised_speed_request(self, conf, have_conf): + intf_name = conf['name'] + request = dict() + + if intf_name.startswith('Eth'): + c_ads = conf.get('advertised_speed', None) + h_ads = have_conf.get('advertised_speed', None) + if c_ads and h_ads: + ads_url = '/openconfig-if-ethernet-ext2:advertised-speed' + eth_url = '/openconfig-if-ethernet:ethernet/config' + config_url = (url + eth_url + ads_url) % quote(intf_name, safe='') + + cc_ads = [value for value in h_ads if value not in c_ads] + if cc_ads: + method = PATCH + adv_speed = ','.join(cc_ads) + payload = {'openconfig-if-ethernet-ext2:advertised-speed': adv_speed} + request = {"path": config_url, "method": method, "data": payload} + + conf['advertised_speed'] = cc_ads + else: + method = DELETE + request = {"path": config_url, "method": method} + + conf['advertised_speed'] = [] + + return request + def filter_comands_to_delete(self, configs, have): commands = [] for conf in configs: if self.is_this_delete_required(conf, have): + intf_name = conf['name'] + temp_conf = dict() temp_conf['name'] = conf['name'] - temp_conf['description'] = '' - temp_conf['mtu'] = 9100 - temp_conf['enabled'] = True - temp_conf['speed'] = 'SPEED_DEFAULT' - temp_conf['auto_negotiate'] = False - temp_conf['fec'] = 'FEC_DISABLED' - temp_conf['advertised_speed'] = '' + if intf_name == 'Management0': + temp_conf['description'] = 'Management0' + temp_conf['mtu'] = 1500 + temp_conf['enabled'] = True + temp_conf['speed'] = None + temp_conf['auto_negotiate'] = None + temp_conf['fec'] = None + else: + temp_conf['description'] = '' + temp_conf['mtu'] = 9100 + temp_conf['enabled'] = False + if intf_name.startswith('Eth'): + temp_conf['speed'] = self.retrieve_default_intf_speed(conf['name']) + temp_conf['auto_negotiate'] = False + temp_conf['fec'] = 'FEC_DISABLED' + else: + temp_conf['speed'] = None + temp_conf['auto_negotiate'] = None + temp_conf['fec'] = None + temp_conf['advertised_speed'] = None commands.append(temp_conf) return commands @@ -277,15 +566,11 @@ def filter_comands_to_change(self, configs, have): def get_modify_interface_requests(self, configs, have): self.delete_flag = False - commands = self.filter_comands_to_change(configs, have) - - return self.get_interface_requests(commands, have) + return self.get_interface_requests(configs, have) def get_delete_interface_requests(self, configs, have): self.delete_flag = True - commands = self.filter_comands_to_delete(configs, have) - - return self.get_interface_requests(commands, have) + return self.get_interface_requests(configs, have) def get_interface_requests(self, configs, have): requests = [] @@ -321,7 +606,8 @@ def get_interface_requests(self, configs, have): if speed_request: requests.append(speed_request) - autoneg_request = self.build_create_autoneg_request(conf) + have_conf = next((cfg for cfg in have if cfg['name'] == name), None) + autoneg_request = self.build_create_autoneg_request(conf, have_conf) if autoneg_request: requests.append(autoneg_request) @@ -329,40 +615,37 @@ def get_interface_requests(self, configs, have): def retrieve_default_intf_speed(self, intf_name): - eth_url = (url + '/openconfig-if-ethernet:ethernet/config/port-speed') % quote(intf_name, safe='') - - # Delete the speed - method = DELETE - request = {"path": eth_url, "method": method} - if not self._module.check_mode: - try: - edit_config(self._module, to_request(self._module, request)) - except ConnectionError as exc: - self._module.fail_json(msg=str(exc), code=exc.code) - - # Read the speed + # Read the valid_speeds + dft_intf_speed = 'SPEED_DEFAULT' method = GET - request = {"path": eth_url, "method": method} + sonic_port_url = 'data/sonic-port:sonic-port/PORT/PORT_LIST=%s' + sonic_port_vs_url = (sonic_port_url + '/valid_speeds') % quote(intf_name, safe='') + request = {"path": sonic_port_vs_url, "method": method} try: response = edit_config(self._module, to_request(self._module, request)) - except ConnectionError as exc: - self._module.fail_json(msg=str(exc), code=exc.code) + if 'sonic-port:valid_speeds' in response[0][1]: + v_speeds = response[0][1].get('sonic-port:valid_speeds', '') + v_speeds_list = v_speeds.split(",") + v_speeds_int_list = [] + for vs in v_speeds_list: + v_speeds_int_list.append(int(vs)) + + dft_speed_int = 0 + if v_speeds_int_list: + dft_speed_int = max(v_speeds_int_list) + dft_intf_speed = intf_speed_map.get(dft_speed_int, 'SPEED_DEFAULT') - intf_speed = 'SPEED_DEFAULT' - if "openconfig-if-ethernet:port-speed" in response[0][1]: - speed_str = response[0][1].get("openconfig-if-ethernet:port-speed", '') - intf_speed = speed_str.split(":", 1)[-1] + except Exception as exc: + pass - return intf_speed + return dft_intf_speed def is_this_delete_required(self, conf, have): - if conf['name'] == "eth0": - return False intf = next((e_intf for e_intf in have if conf['name'] == e_intf['name']), None) if intf: if (intf['name'].startswith('Loopback') or not ((intf.get('description') is None or intf.get('description') == '') and - (intf.get('enabled') is None or intf.get('enabled') is True) and + (intf.get('enabled') is None or intf.get('enabled') is False) and (intf.get('mtu') is None or intf.get('mtu') == 9100) and (intf.get('fec') is None or intf.get('fec') == 'FEC_DISABLED') and (intf.get('speed') is None or @@ -373,8 +656,6 @@ def is_this_delete_required(self, conf, have): return False def is_this_change_required(self, conf, have): - if conf['name'] == "eth0": - return False ret_flag = False intf = next((e_intf for e_intf in have if conf['name'] == e_intf['name']), None) if intf: @@ -446,7 +727,7 @@ def build_create_speed_request(self, conf): return request - def build_create_autoneg_request(self, conf): + def build_create_autoneg_request(self, conf, have_conf): intf_name = conf['name'] eth_conf = dict() request = dict() @@ -459,8 +740,14 @@ def build_create_autoneg_request(self, conf): eth_conf['auto-negotiate'] = True else: eth_conf['auto-negotiate'] = False - if conf.get('advertised_speed') is not None: - eth_conf['openconfig-if-ethernet-ext2:advertised-speed'] = ','.join(conf['advertised_speed']) + + c_ads = conf.get('advertised_speed', []) + if c_ads: + h_ads = have_conf.get('advertised_speed', []) + if h_ads is None: + h_ads = [] + new_ads = h_ads + c_ads + eth_conf['openconfig-if-ethernet-ext2:advertised-speed'] = ','.join(new_ads) if eth_conf: eth_url = (url + '/openconfig-if-ethernet:ethernet/config') % quote(intf_name, safe='') @@ -468,3 +755,30 @@ def build_create_autoneg_request(self, conf): request = {"path": eth_url, "method": method, "data": payload} return request + + def classify_delete_commands(self, configs): + commands_del = [] + commands_mer = [] + + if not configs: + return commands_del, commands_mer + + for conf in configs: + name = conf["name"] + if name.startswith('Loopback'): + commands_del.append(conf) + else: + commands_mer.append(conf) + + return commands_del, commands_mer + + def filter_out_mgmt_interface(self, want, have): + if want: + mgmt_intf = next((intf for intf in want if intf['name'] == 'Management0'), None) + if mgmt_intf: + self._module.fail_json(msg='Management interface should not be configured.') + + for intf in have: + if intf['name'] == 'Management0': + have.remove(intf) + break diff --git a/plugins/module_utils/network/sonic/config/port_group/port_group.py b/plugins/module_utils/network/sonic/config/port_group/port_group.py index e9eba8a67..f1f7bb107 100644 --- a/plugins/module_utils/network/sonic/config/port_group/port_group.py +++ b/plugins/module_utils/network/sonic/config/port_group/port_group.py @@ -13,6 +13,14 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type +""" +The use of natsort causes sanity error due to it is not available in python version currently used. +When natsort becomes available, the code here and below using it will be applied. +from natsort import ( + natsorted, + ns +) +""" from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -31,6 +39,11 @@ update_states, remove_empties_from_list ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff +) from ansible.module_utils.connection import ConnectionError GET = "get" @@ -43,6 +56,9 @@ 'config': {'id': ''} } ] +TEST_KEYS_formatted_diff = [ + {'config': {'id': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}} +] class Port_group(ConfigBase): @@ -107,6 +123,18 @@ def execute_module(self): if result['changed']: result['after'] = changed_port_group_facts + new_config = changed_port_group_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_port_group_facts, + TEST_KEYS_formatted_diff) + # See the above comment about natsort module + # new_config = natsorted(new_config, key=lambda x: x['id']) + result['after(generated)'] = new_config + + if self._module._diff: + result['config_diff'] = get_formatted_config_diff(existing_port_group_facts, + new_config) result['warnings'] = warnings return result diff --git a/plugins/module_utils/network/sonic/config/system/system.py b/plugins/module_utils/network/sonic/config/system/system.py index 249227c73..5e30004ba 100644 --- a/plugins/module_utils/network/sonic/config/system/system.py +++ b/plugins/module_utils/network/sonic/config/system/system.py @@ -33,11 +33,38 @@ to_request, edit_config ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + get_new_config, + get_formatted_config_diff +) PATCH = 'patch' DELETE = 'delete' +def __derive_system_config_delete_op(key_set, command, exist_conf): + new_conf = exist_conf + + if 'hostname' in command: + new_conf['hostname'] = 'sonic' + if 'interface_naming' in command: + new_conf['interface_naming'] = 'native' + if 'anycast_address' in command and 'anycast_address' in new_conf: + if 'ipv4' in command['anycast_address']: + new_conf['anycast_address']['ipv4'] = True + if 'ipv6' in command['anycast_address']: + new_conf['anycast_address']['ipv6'] = True + if 'mac_address' in command['anycast_address']: + new_conf['anycast_address']['mac_address'] = None + + return True, new_conf + + +TEST_KEYS_formatted_diff = [ + {'__delete_op_default': {'__delete_op': __derive_system_config_delete_op}}, +] + + class System(ConfigBase): """ The sonic_system class @@ -91,6 +118,16 @@ def execute_module(self): if result['changed']: result['after'] = changed_system_facts + new_config = changed_system_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_system_facts, + TEST_KEYS_formatted_diff) + result['after(generated)'] = new_config + + if self._module._diff: + result['config_diff'] = get_formatted_config_diff(existing_system_facts, + new_config) result['warnings'] = warnings return result diff --git a/plugins/module_utils/network/sonic/config/users/users.py b/plugins/module_utils/network/sonic/config/users/users.py index 1bc80809f..6d93ba7a7 100644 --- a/plugins/module_utils/network/sonic/config/users/users.py +++ b/plugins/module_utils/network/sonic/config/users/users.py @@ -82,7 +82,7 @@ def execute_module(self): except ConnectionError as exc: try: json_obj = json.loads(str(exc).replace("'", '"')) - if json_obj and type(json_obj) is dict and 401 == json_obj['code']: + if json_obj and isinstance(json_obj, dict) and 401 == json_obj['code']: auth_error = True warnings.append("Unable to get after configs as password got changed for current user") else: diff --git a/plugins/module_utils/network/sonic/config/vlans/vlans.py b/plugins/module_utils/network/sonic/config/vlans/vlans.py index 651652919..58d7449b9 100644 --- a/plugins/module_utils/network/sonic/config/vlans/vlans.py +++ b/plugins/module_utils/network/sonic/config/vlans/vlans.py @@ -35,12 +35,20 @@ to_request, edit_config ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( + __DELETE_CONFIG_IF_NO_SUBCONFIG, + get_new_config, + get_formatted_config_diff +) from ansible.module_utils.connection import ConnectionError TEST_KEYS = [ {'config': {'vlan_id': ''}}, ] +TEST_KEYS_formatted_diff = [ + {'config': {'vlan_id': '', '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}, +] class Vlans(ConfigBase): @@ -98,6 +106,17 @@ def execute_module(self): if result['changed']: result['after'] = changed_vlans_facts + new_config = changed_vlans_facts + if self._module.check_mode: + result.pop('after', None) + new_config = get_new_config(commands, existing_vlans_facts, + TEST_KEYS_formatted_diff) + new_config.sort(key=lambda x: x['vlan_id']) + result['after(generated)'] = new_config + + if self._module._diff: + result['config_diff'] = get_formatted_config_diff(existing_vlans_facts, + new_config) result['warnings'] = warnings return result diff --git a/plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py b/plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py index aa23e71b4..a673a7071 100644 --- a/plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py +++ b/plugins/module_utils/network/sonic/facts/bgp_communities/bgp_communities.py @@ -66,26 +66,34 @@ def get_bgp_communities(self): match = member_config['match-set-options'] permit_str = member_config.get('openconfig-bgp-policy-ext:action', None) members = member_config.get("community-member", []) - result['name'] = name + result['name'] = str(name) result['match'] = match + result['members'] = None + result['permit'] = False if permit_str and permit_str == 'PERMIT': result['permit'] = True - else: - result['permit'] = False if members: result['type'] = 'expanded' if 'REGEX' in members[0] else 'standard' - else: - result['type'] = '' if result['type'] == 'expanded': members = [':'.join(i.split(':')[1:]) for i in members] - result['local_as'] = True if "NO_EXPORT_SUBCONFED" in members else False - result['no_advertise'] = True if "NO_ADVERTISE" in members else False - result['no_export'] = True if "NO_EXPORT" in members else False - result['no_peer'] = True if "NOPEER" in members else False - result['members'] = {'regex': members} + members.sort() + result['members'] = {'regex': members} + else: + result['local_as'] = None + result['no_advertise'] = None + result['no_export'] = None + result['no_peer'] = None + for i in members: + if "NO_EXPORT_SUBCONFED" in i: + result['local_as'] = True + elif "NO_ADVERTISE" in i: + result['no_advertise'] = True + elif "NO_EXPORT" in i: + result['no_export'] = True + elif "NOPEER" in i: + result['no_peer'] = True + bgp_communities_configs.append(result) - # with open('/root/ansible_log.log', 'a+') as fp: - # fp.write('bgp_communities: ' + str(bgp_communities_configs) + '\n') return bgp_communities_configs def populate_facts(self, connection, ansible_facts, data=None): @@ -128,17 +136,5 @@ def render_config(self, spec, conf): :rtype: dictionary :returns: The generated config """ - config = deepcopy(spec) - try: - config['name'] = str(conf['name']) - config['members'] = conf['members'] - config['match'] = conf['match'] - config['type'] = conf['type'] - config['permit'] = conf['permit'] - except TypeError: - config['name'] = None - config['members'] = None - config['match'] = None - config['type'] = None - config['permit'] = None - return utils.remove_empties(config) + + return conf diff --git a/plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py b/plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py index 6f9b104ac..814a25d11 100644 --- a/plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py +++ b/plugins/module_utils/network/sonic/facts/bgp_ext_communities/bgp_ext_communities.py @@ -68,34 +68,38 @@ def get_bgp_extcommunities(self): match = member_config['match-set-options'] permit_str = member_config.get('openconfig-bgp-policy-ext:action', None) members = member_config.get("ext-community-member", []) - result['name'] = name + result['name'] = str(name) result['match'] = match.lower() - + result['members'] = dict() + result['type'] = 'standard' + result['permit'] = False if permit_str and permit_str == 'PERMIT': result['permit'] = True + if members: + result['type'] = 'expanded' if 'REGEX' in members[0] else 'standard' + if result['type'] == 'expanded': + members = [':'.join(i.split(':')[1:]) for i in members] + members_list = list(map(str, members)) + members_list.sort() + result['members'] = {'regex': members_list} else: - result['permit'] = False - - result['members'] = dict() - rt = list() - soo = list() - regex = list() - for member in members: - if member.startswith('route-target'): - rt.append(':'.join(member.split(':')[1:])) - elif member.startswith('route-origin'): - soo.append(':'.join(member.split(':')[1:])) - elif member.startswith('REGEX'): - regex.append(':'.join(member.split(':')[1:])) - - result['type'] = 'standard' - if regex and len(regex) > 0: - result['type'] = 'expanded' - result['members']['regex'] = regex - if rt and len(rt) > 0: - result['members']['route_target'] = rt - if soo and len(soo) > 0: - result['members']['route_origin'] = soo + rt = list() + soo = list() + for member in members: + if member.startswith('route-origin'): + soo.append(':'.join(member.split(':')[1:])) + else: + rt.append(':'.join(member.split(':')[1:])) + route_target_list = list(map(str, rt)) + route_origin_list = list(map(str, soo)) + route_target_list.sort() + route_origin_list.sort() + + if route_target_list and len(route_target_list) > 0: + result['members']['route_target'] = route_target_list + + if route_origin_list and len(route_origin_list) > 0: + result['members']['route_origin'] = route_origin_list bgp_extcommunities_configs.append(result) @@ -141,17 +145,5 @@ def render_config(self, spec, conf): :rtype: dictionary :returns: The generated config """ - config = deepcopy(spec) - try: - config['name'] = str(conf['name']) - config['members'] = conf['members'] - config['match'] = conf['match'] - config['type'] = conf['type'] - config['permit'] = conf['permit'] - except TypeError: - config['name'] = None - config['members'] = None - config['match'] = None - config['type'] = None - config['permit'] = None - return utils.remove_empties(config) + + return conf diff --git a/plugins/module_utils/network/sonic/facts/interfaces/interfaces.py b/plugins/module_utils/network/sonic/facts/interfaces/interfaces.py index 610405a50..4773dd843 100644 --- a/plugins/module_utils/network/sonic/facts/interfaces/interfaces.py +++ b/plugins/module_utils/network/sonic/facts/interfaces/interfaces.py @@ -134,7 +134,7 @@ def transform_config(self, conf): trans_cfg['description'] = exist_cfg['description'] if exist_cfg.get('description') else "" trans_cfg['mtu'] = exist_cfg['mtu'] if exist_cfg.get('mtu') else 9100 - if name.startswith('Ethernet'): + if name.startswith('Eth') and 'openconfig-if-ethernet:ethernet' in conf: eth_conf = conf['openconfig-if-ethernet:ethernet']['config'] if 'port-speed' in eth_conf: trans_cfg['speed'] = eth_conf['port-speed'].split(':', 1)[-1] diff --git a/plugins/module_utils/network/sonic/facts/stp/stp.py b/plugins/module_utils/network/sonic/facts/stp/stp.py index 5b436ff3a..da779c502 100644 --- a/plugins/module_utils/network/sonic/facts/stp/stp.py +++ b/plugins/module_utils/network/sonic/facts/stp/stp.py @@ -355,7 +355,7 @@ def convert_vlans_list(self, vlans): converted_vlans = [] for vlan in vlans: - if type(vlan) is int: + if isinstance(vlan, int): converted_vlans.append(str(vlan)) else: diff --git a/plugins/module_utils/network/sonic/sonic.py b/plugins/module_utils/network/sonic/sonic.py index 60bd3d491..30739ef82 100644 --- a/plugins/module_utils/network/sonic/sonic.py +++ b/plugins/module_utils/network/sonic/sonic.py @@ -131,7 +131,7 @@ def edit_config(module, commands, skip_code=None): # Start: This is to convert interface name from Eth1/1 to Eth1%2f1 for request in commands: # This check is to differenciate between requests and commands - if type(request) is dict: + if isinstance(request, dict): url = request.get("path", None) if url: request["path"] = update_url(url) diff --git a/plugins/module_utils/network/sonic/utils/utils.py b/plugins/module_utils/network/sonic/utils/utils.py index b1564804f..be36d64e2 100644 --- a/plugins/module_utils/network/sonic/utils/utils.py +++ b/plugins/module_utils/network/sonic/utils/utils.py @@ -471,7 +471,7 @@ def get_breakout_mode(module, name): except ConnectionError as exc: try: json_obj = json.loads(str(exc).replace("'", '"')) - if json_obj and type(json_obj) is dict and 404 == json_obj['code']: + if json_obj and isinstance(json_obj, dict) and 404 == json_obj['code']: response = None else: module.fail_json(msg=str(exc), code=exc.code) diff --git a/plugins/modules/sonic_bgp_communities.py b/plugins/modules/sonic_bgp_communities.py index 08c8dcc7f..dd1c2b083 100644 --- a/plugins/modules/sonic_bgp_communities.py +++ b/plugins/modules/sonic_bgp_communities.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -52,7 +52,7 @@ required: True type: str description: - - Name of the BGP communitylist. + - Name of the BGP community-list. type: type: str description: @@ -67,6 +67,7 @@ type: bool description: - Permits or denies this community. + - Default value while adding a new community-list is C(False). aann: required: False type: str @@ -120,6 +121,8 @@ choices: - merged - deleted + - replaced + - overridden default: merged """ EXAMPLES = """ @@ -130,18 +133,21 @@ # # show bgp community-list # Standard community list test: match: ANY -# 101 -# 201 -# Standard community list test1: match: ANY -# 301 +# permit local-as +# permit no-peer +# Expanded community list test1: match: ANY +# deny 101 +# deny 302 -- name: Deletes BGP community member +- name: Delete a BGP community-list member dellemc.enterprise_sonic.sonic_bgp_communities: config: - - name: test + - name: test1 + type: expanded + permit: false members: regex: - - 201 + - 302 state: deleted # After state: @@ -149,9 +155,10 @@ # # show bgp community-list # Standard community list test: match: ANY -# 101 -# Standard community list test1: match: ANY -# 301 +# permit local-as +# permit no-peer +# Expanded community list test1: match: ANY +# deny 101 # Using deleted @@ -161,15 +168,17 @@ # # show bgp community-list # Standard community list test: match: ANY -# 101 +# permit local-as +# permit no-peer # Expanded community list test1: match: ANY -# 201 +# deny 101 +# deny 302 -- name: Deletes a single BGP community +- name: Delete a single BGP community-list dellemc.enterprise_sonic.sonic_bgp_communities: config: - name: test - members: + type: standard state: deleted # After state: @@ -177,7 +186,8 @@ # # show bgp community-list # Expanded community list test1: match: ANY -# 201 +# deny 101 +# deny 302 # Using deleted @@ -187,11 +197,13 @@ # # show bgp community-list # Standard community list test: match: ANY -# 101 +# permit local-as +# permit no-peer # Expanded community list test1: match: ANY -# 201 +# deny 101 +# deny 302 -- name: Delete All BGP communities +- name: Delete All BGP community-lists dellemc.enterprise_sonic.sonic_bgp_communities: config: state: deleted @@ -210,14 +222,17 @@ # # show bgp community-list # Standard community list test: match: ANY -# 101 +# permit local-as +# permit no-peer # Expanded community list test1: match: ANY -# 201 +# deny 101 +# deny 302 -- name: Deletes all members in a single BGP community +- name: Delete all members in a single BGP community-list dellemc.enterprise_sonic.sonic_bgp_communities: config: - - name: test + - name: test1 + type: expanded members: regex: state: deleted @@ -226,9 +241,9 @@ # ------------ # # show bgp community-list -# Expanded community list test: match: ANY -# Expanded community list test1: match: ANY -# 201 +# Standard community list test: match: ANY +# permit local-as +# permit no-peer # Using merged @@ -236,23 +251,105 @@ # Before state: # ------------- # -# show bgp as-path-access-list -# AS path list test: +# show bgp community-list +# Expanded community list test1: match: ANY +# permit 101 +# permit 302 -- name: Adds 909.* to test as-path list - dellemc.enterprise_sonic.sonic_bgp_as_paths: +- name: Add a new BGP community-list + dellemc.enterprise_sonic.sonic_bgp_communities: config: - - name: test + - name: test2 + type: expanded + permit: true members: - - 909.* + regex: + - 909 state: merged # After state: # ------------ # -# show bgp as-path-access-list -# AS path list test: -# members: 909.* +# show bgp community-list +# Expanded community list test1: match: ANY +# permit 101 +# permit 302 +# Expanded community list test2: match: ANY +# permit 909 + + +# Using replaced + +# Before state: +# ------------- +# +# show bgp community-list +# Standard community list test: match: ANY +# permit local-as +# permit no-peer +# Expanded community list test1: match: ANY +# deny 101 +# deny 302 + +- name: Replacing a single BGP community-list + dellemc.enterprise_sonic.sonic_bgp_communities: + config: + - name: test + type: expanded + members: + regex: + - 301 + - name: test3 + type: standard + no_advertise: true + no_peer: true + permit: false + match: ALL + state: replaced + +# After state: +# ------------ +# +# show bgp community-list +# Expanded community list test: match: ANY +# deny 301 +# Expanded community list test1: match: ANY +# deny 101 +# deny 302 +# Standard community list test3: match: ALL +# deny no-advertise +# deny no-peer + + +# Using overridden + +# Before state: +# ------------- +# +# show bgp community-list +# Standard community list test: match: ANY +# permit local-as +# permit no-peer +# Expanded community list test1: match: ANY +# deny 101 +# deny 302 + +- name: Override entire BGP community-lists + dellemc.enterprise_sonic.sonic_bgp_communities: + config: + - name: test3 + type: expanded + members: + regex: + - 301 + state: overridden + +# After state: +# ------------ +# +# show bgp community-list +# Expanded community list test3: match: ANY +# deny 301 """ diff --git a/plugins/modules/sonic_bgp_ext_communities.py b/plugins/modules/sonic_bgp_ext_communities.py index c2af0c488..49a30c9f9 100644 --- a/plugins/modules/sonic_bgp_ext_communities.py +++ b/plugins/modules/sonic_bgp_ext_communities.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2020 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -66,6 +66,7 @@ type: bool description: - Permits or denies this community. + - Default value while adding a new ext-community-list is False. members: required: False type: dict @@ -106,6 +107,8 @@ choices: - merged - deleted + - replaced + - overridden default: merged """ EXAMPLES = """ @@ -116,15 +119,16 @@ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# rt:101:101 -# rt:201:201 +# permit rt:101:101 +# permit rt:201:201 - name: Deletes a BGP ext community member dellemc.enterprise_sonic.sonic_bgp_ext_communities: config: - name: test + type: standard members: - regex: + route_target: - 201:201 state: deleted @@ -133,7 +137,7 @@ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# rt:101:101 +# permit rt:101:101 # @@ -144,9 +148,10 @@ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# 101 -# Expanded extended community list test1: match: ANY -# 201 +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 - name: Deletes a single BGP extended community dellemc.enterprise_sonic.sonic_bgp_ext_communities: @@ -160,7 +165,8 @@ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# 101 +# permit rt:101:101 +# permit rt:201:201 # @@ -171,9 +177,10 @@ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# 101 -# Expanded extended community list test1: match: ANY -# 201 +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 - name: Deletes all BGP extended communities dellemc.enterprise_sonic.sonic_bgp_ext_communities: @@ -194,9 +201,10 @@ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# 101 -# Expanded extended community list test1: match: ANY -# 201 +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 - name: Deletes all members in a single BGP extended community dellemc.enterprise_sonic.sonic_bgp_ext_communities: @@ -211,8 +219,8 @@ # # show bgp ext-community-list # Standard extended community list test: match: ANY -# 101 -# Expanded extended community list test1: match: ANY +# permit rt:101:101 +# permit rt:201:201 # @@ -221,23 +229,108 @@ # Before state: # ------------- # -# show bgp as-path-access-list -# AS path list test: +# show bgp ext-community-list +# Standard extended community list test: match: ANY +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 -- name: Adds 909.* to test as-path list - dellemc.enterprise_sonic.sonic_bgp_as_paths: +- name: Adds new community list + dellemc.enterprise_sonic.sonic_bgp_ext_communities: config: - - name: test + - name: test3 + type: standard + match: any + permit: true members: - - 909.* + route_origin: + - "301:301" + - "401:401" state: merged # After state: # ------------ # -# show bgp as-path-access-list -# AS path list test: -# members: 909.* +# show bgp ext-community-list +# Standard extended community list test: match: ANY +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 +# Standard extended community list test3: match: ANY +# permit soo:301:301 +# permit soo:401:401 + + + +# Using replaced + +# Before state: +# ------------- +# +# show bgp ext-community-list +# Standard extended community list test: match: ANY +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 + +- name: Replacing a single BGP extended community + dellemc.enterprise_sonic.sonic_bgp_ext_communities: + config: + - name: test + type: expanded + permit: true + match: all + members: + regex: + - 301:302 + state: replaced + +# After state: +# ------------ +# +# show bgp ext-community-list +# Expanded extended community list test: match: ALL +# permit 301:302 +# Expanded extended community list test1: match: ALL +# deny 101:102 +# + + +# Using overridden + +# Before state: +# ------------- +# +# show bgp ext-community-list +# Standard extended community list test: match: ANY +# permit rt:101:101 +# permit rt:201:201 +# Expanded extended community list test1: match: ALL +# deny 101:102 + + +- name: Override the entire list of BGP extended community + dellemc.enterprise_sonic.sonic_bgp_ext_communities: + config: + - name: test3 + type: expanded + permit: true + match: all + members: + regex: + - 301:302 + state: overridden + +# After state: +# ------------ +# +# show bgp ext-community-list +# Expanded extended community list test3: match: ALL +# permit 301:302 +# """ diff --git a/tests/regression/roles/sonic_bgp_communities/defaults/main.yml b/tests/regression/roles/sonic_bgp_communities/defaults/main.yml index eb32d2759..cf27073be 100644 --- a/tests/regression/roles/sonic_bgp_communities/defaults/main.yml +++ b/tests/regression/roles/sonic_bgp_communities/defaults/main.yml @@ -9,7 +9,7 @@ tests: input: - name: test type: expanded - permit: false + permit: true match: ANY members: regex: @@ -17,19 +17,16 @@ tests: - "12" - name: test2 type: standard - permit: true + permit: false + no_export: true match: ALL - members: - regex: - - "21" - - "22" - name: test_case_02 description: Update created BGP properties state: merged input: - name: test type: expanded - permit: false + permit: true match: ANY members: regex: @@ -38,20 +35,16 @@ tests: - 14 - name: test2 type: standard - permit: true + permit: false + no_peer: true match: ALL - members: - regex: - - "23" - - "24" - - 25 - name: test_case_03 description: Update1 created BGP properties state: merged input: - name: test type: expanded - permit: true + permit: false match: ANY members: regex: @@ -59,12 +52,8 @@ tests: - "12" - name: test2 type: standard - permit: false + permit: true match: ALL - members: - regex: - - "21" - - "22" - name: test_case_04 description: Delete BGP properties state: deleted @@ -78,10 +67,7 @@ tests: - name: test2 type: standard match: ALL - members: - regex: - - "23" - - "24" + no_export: true - name: test_case_05 description: Delete1 BGP properties state: deleted @@ -91,11 +77,124 @@ tests: members: regex: - name: test_case_06 - description: Delete2 BGP properties - state: deleted + description: Update2 BGP properties + state: merged input: - name: test + type: expanded + match: ANY + permit: true + members: + regex: + - 201 + - name: test3 + type: expanded + match: ALL + permit: true + members: + regex: + - "110" + - 111 - name: test_case_07 + description: Replace BGP properties + state: replaced + input: + - name: test + type: standard + local_as: true + permit: true + - name: test2 + type: expanded + match: ALL + permit: false + members: + regex: + - "220" + - 222 + - "123" + - name: test_case_08 + description: Replace2 BGP properties + state: replaced + input: + - name: test4 + type: standard + permit: true + no_peer: true + - name: test5 + type: expanded + members: + regex: + - 113 + permit: true + - name: test_case_09 + description: Override BGP properties + state: overridden + input: + - name: test3 + type: standard + local_as: True + permit: false + - name: test2 + type: standard + permit: true + no_export: true + - name: test_case_10 + description: Override2 BGP properties + state: overridden + input: + - name: test3 + type: standard + permit: false + no_export: true + - name: test4 + type: expanded + permit: false + members: + regex: + - 113 + - name: test2 + type: standard + permit: true + no_export: true + - name: test_case_11 + description: Override3 BGP properties + state: overridden + input: + - name: test4 + type: expanded + permit: false + members: + regex: + - 113 + - name: test2 + type: standard + local_as: true + no_peer: true + no_advertise: true + permit: true + no_export: true + - name: test_case_12 + description: Override4 BGP properties + state: overridden + input: + - name: test4 + type: expanded + permit: false + members: + regex: + - 113 + - name: test2 + type: standard + local_as: true + no_advertise: true + permit: true + no_export: true + - name: test_case_13 + description: Delete2 BGP properties + state: deleted + input: + - name: test4 + - name: test_case_14 description: Delete2 BGP properties state: deleted input: [] diff --git a/tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml b/tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml index be6e96a86..6e9daf9a1 100644 --- a/tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml +++ b/tests/regression/roles/sonic_bgp_ext_communities/defaults/main.yml @@ -2,7 +2,7 @@ ansible_connection: httpapi module_name: sonic_bgp_ext_communities -tests: "{{ merged_tests + deleted_tests }}" +tests: "{{ merged_tests + deleted_tests + replaced_tests + overridden_tests + deleted_all }}" merged_tests: - name: test_case_01 @@ -95,19 +95,25 @@ merged_tests: match: all members: route_origin: - - "4403.301" - - "5503.401" + - "4403:301" + - "5503:401" - name: test_case_05 - description: Create empty Communities properties + description: Create new Communities properties state: merged input: - name: test_ext1 type: expanded permit: true + members: + regex: + - "20" match: any - name: test_std1 type: standard permit: false + members: + route_target: + - "1.1.1.1:33" match: any - name: test_case_06 description: test BGP Communities properties @@ -127,8 +133,8 @@ merged_tests: match: any members: route_origin: - - "4403.301" - - "5503.401" + - "4403:301" + - "5503:401" deleted_tests: # Ethernet testcases started... @@ -154,101 +160,65 @@ deleted_tests: match: all members: route_target: - - "101.101" - - "201.201" - - "102.101" - - "202.201" - - "1.1.1.1.101" - - "1.1.1.2.201" + - "101:101" + - "201:201" + - "102:101" + - "202:201" + - "1.1.1.1:101" + - "1.1.1.2:201" route_origin: - - "301.301" - - "401.401" - - "302.301" - - "402.401" - - "303.301" - - "403.401" + - "301:301" + - "401:401" + - "302:301" + - "402:401" + - "303:301" + - "403:401" - name: test_std11 type: standard permit: true match: all members: route_target: - - "101.101" - - "201.201" - - "102.101" - - "202.201" - - "103.101" - - "203.201" - - "1.1.1.1.101" - - "1.1.1.2.201" - - "1.1.1.1.102" - - "1.1.1.2.203" - route_origin: - - "301.301" - - "401.401" - - "302.301" - - "402.401" - - "303.301" - - "403.401" - - name: test_std12 - type: standard - permit: true - match: all - members: - route_target: - - "101.101" - - "201.201" - - "102.101" - - "202.201" - - "103.101" - - "203.201" - - "1.1.1.1.101" - - "1.1.1.2.201" - - "1.1.1.1.102" - - "1.1.1.2.203" - route_origin: - - "301.301" - - "401.401" - - "302.301" - - "402.401" - - "303.301" - - "403.401" - - name: test_std12 - type: standard - permit: true - match: all - members: - route_target: - - "101.101" - - "201.201" - - "102.101" - - "202.201" - - "103.101" - - "203.201" - - "1.1.1.1.101" - - "1.1.1.2.201" - - "1.1.1.1.102" - - "1.1.1.2.203" + - "101:101" + - "201:201" + - "102:101" + - "202:201" + - "103:101" + - "203:201" + - "1.1.1.1:101" + - "1.1.1.2:201" + - "1.1.1.1:102" + - "1.1.1.2:203" route_origin: - - "301.301" - - "401.401" - - "302.301" - - "402.401" - - "303.301" - - "403.401" + - "301:301" + - "401:401" + - "302:301" + - "402:401" + - "303:301" + - "403:401" - name: test_std12 type: standard permit: true match: all members: route_target: - - "103.101" - - "203.201" + - "101:101" + - "201:201" + - "102:101" + - "202:201" + - "103:101" + - "203:201" + - "1.1.1.1:101" + - "1.1.1.2:201" - "1.1.1.1:102" - - "1.1.1.2.203" + - "1.1.1.2:203" route_origin: - - "301.301" - - "401.401" + - "301:301" + - "401:401" + - "302:301" + - "402:401" + - "303:301" + - "403:401" - name: del_test_case_01 description: BGP Communities properties state: deleted @@ -277,7 +247,7 @@ deleted_tests: route_origin: - "301:301" - "401:401" - - name: test_case_02 + - name: del_test_case_02 description: BGP Communities properties state: deleted input: @@ -289,14 +259,15 @@ deleted_tests: type: standard members: route_target: - - "1.1.1.1.101" + - "1.1.1.1:101" - name: test_std11 type: standard members: route_origin: - - "301.301" - - "401.401" - - "1.1.1.1.101" + - "301:301" + - "401:401" + route_target: + - "1.1.1.1:101" - name: del_test_case_03 description: Update created BGP properties state: deleted @@ -315,6 +286,150 @@ deleted_tests: members: route_origin: route_target: + +replaced_tests: + - name: replace_test_case_01 + description: Replace created BGP properties + state: replaced + input: + - name: test_std + type: standard + permit: false + match: any + members: + route_origin: + - "4403:301" + - "5503:401" + - name: test_comm112 + type: standard + permit: true + match: any + members: + route_target: + - "2.2.2.2:201" + route_origin: + - "500:500" + - "600:600" + - name: replace_test_case_02 + description: Replace2 created BGP properties + state: replaced + input: + - name: test_comm13 + type: expanded + permit: true + match: all + members: + regex: + - "15" + - "20" + - 25 + - name: test_comm112 + type: standard + permit: true + match: any + members: + route_target: + - "2.2.2.2:201" + route_origin: + - "500:500" + - "600:600" + - name: test_std + type: standard + permit: false + match: any + members: + route_origin: + - "5503:401" + +overridden_tests: + - name: overridden_test_case_01 + description: Override created BGP properties + state: overridden + input: + - name: test_new_std + type: standard + permit: true + match: all + members: + route_target: + - "12.12.12.12:335" + - "13.13.13.13:445" + route_origin: + - "4301:4301" + - "501:501" + - name: test_new_expanded + type: expanded + permit: false + match: any + members: + regex: + - 22 + - 23 + - 24 + - name: overridden_test_case_02 + description: Override2 created BGP properties + state: overridden + input: + - name: test_new_expanded2 + type: expanded + permit: true + members: + regex: + - 33 + - "44" + - name: test_new_std + type: standard + permit: true + match: all + members: + route_target: + - "13.13.13.13:445" + route_origin: + - "501:501" + - name: test_new_expanded + type: expanded + permit: false + match: any + members: + regex: + - 22 + - 23 + - 24 + - name: overridden_test_case_03 + description: Override3 created BGP properties + state: overridden + input: + - name: test_new_std + type: standard + permit: true + match: all + members: + route_target: + - "13.13.13.13:445" + route_origin: + - "501:501" + - name: test_new_expanded + type: expanded + permit: false + match: any + members: + regex: + - 22 + - 23 + - 24 + - name: overridden_test_case_04 + description: Override4 created BGP properties + state: overridden + input: + - name: test_new_std + type: standard + permit: true + match: all + members: + route_origin: + - "502:502" + +deleted_all: - name: del_test_case_04 description: Update created BGP properties state: deleted diff --git a/tests/unit/modules/network/sonic/fixtures/sonic_bgp_communities.yaml b/tests/unit/modules/network/sonic/fixtures/sonic_bgp_communities.yaml index 3493ff6ca..cb827bfe0 100644 --- a/tests/unit/modules/network/sonic/fixtures/sonic_bgp_communities.yaml +++ b/tests/unit/modules/network/sonic/fixtures/sonic_bgp_communities.yaml @@ -6,12 +6,7 @@ merged_01: members: regex: - 808.* - aann: aann_test_str - local_as: False match: ALL - no_advertise: True - no_export: True - no_peer: True permit: True type: expanded existing_bgp_config: @@ -42,15 +37,14 @@ merged_01: community-set-name: 'test' community-member: - REGEX:808.* - - REGEX:NOPEER - - REGEX:NO_EXPORT - - REGEX:NO_ADVERTISE openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' merged_02: module_args: config: - name: test + type: standard + permit: True local_as: False no_export: True no_peer: True @@ -65,10 +59,7 @@ merged_02: config: community-set-name: 'test' community-member: - - REGEX:808.* - - REGEX:NOPEER - - REGEX:NO_EXPORT - - REGEX:NO_ADVERTISE + - NO_ADVERTISE openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' expected_config_requests: @@ -81,9 +72,6 @@ merged_02: config: community-set-name: 'test' community-member: - - 808.* - - NOPEER - - NO_EXPORT - NO_ADVERTISE - NOPEER - NO_EXPORT @@ -93,6 +81,7 @@ deleted_01: module_args: config: - name: test + type: expanded members: regex: - 808.* @@ -110,9 +99,9 @@ deleted_01: community-set-name: 'test' community-member: - REGEX:808.* - - REGEX:NOPEER - - REGEX:NO_EXPORT - - REGEX:NO_ADVERTISE + - REGEX:919.* + - REGEX:930.* + - REGEX:772.* openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" @@ -122,14 +111,22 @@ deleted_01: sonic-vrf:VRF_LIST: - vrf_name: default expected_config_requests: - - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test/config/community-member=808.%2A" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test/config/community-member=REGEX%3A808.%2A" method: "delete" deleted_02: module_args: config: - name: test + type: expanded + match: ALL + permit: True members: - regex: + regex: + - name: test2 + type: standard + match: ANY + permit: False + local_as: True state: deleted existing_bgp_config: - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" @@ -143,11 +140,21 @@ deleted_02: community-set-name: 'test' community-member: - REGEX:808.* - - REGEX:NOPEER - - REGEX:NO_EXPORT - - REGEX:NO_ADVERTISE + - REGEX:919.* + - REGEX:700.* + - REGEX:888.* openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' + - community-set-name: 'test2' + config: + community-set-name: 'test2' + community-member: + - NO_ADVERTISE + - NOPEER + - NO_EXPORT_SUBCONFED + - NO_EXPORT + openconfig-bgp-policy-ext:action: 'DENY' + match-set-options: 'ANY' - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" response: code: 200 @@ -155,7 +162,9 @@ deleted_02: sonic-vrf:VRF_LIST: - vrf_name: default expected_config_requests: - - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test/config/community-member" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test2/config/community-member=NO_EXPORT_SUBCONFED" method: "delete" deleted_03: module_args: @@ -171,11 +180,112 @@ deleted_03: - community-set-name: 'test' config: community-set-name: 'test' + community-member: + - NOPEER + - NO_EXPORT + - NO_ADVERTISE + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + method: "delete" + +replaced_01: + module_args: + config: + - name: test + members: + regex: + - 808.* + match: ALL + permit: True + type: expanded + state: replaced + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - NO_ADVERTISE + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' + - community-set-name: 'test2' + config: + community-set-name: 'test2' + community-member: + - REGEX:808.* + - REGEX:919.* + - REGEX:700.* + - REGEX:888.* + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + method: "patch" + data: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - REGEX:808.* + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + +replaced_02: + module_args: + config: + - name: test2 + members: + regex: + - 808.* + match: ALL + permit: False + type: expanded + state: replaced + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - NO_ADVERTISE + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' + - community-set-name: 'test2' + config: + community-set-name: 'test2' community-member: - REGEX:808.* - - REGEX:NOPEER - - REGEX:NO_EXPORT - - REGEX:NO_ADVERTISE + - REGEX:919.* + - REGEX:700.* + - REGEX:888.* openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" @@ -185,5 +295,88 @@ deleted_03: sonic-vrf:VRF_LIST: - vrf_name: default expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test2" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + method: "patch" + data: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test2' + config: + community-set-name: 'test2' + community-member: + - REGEX:808.* + openconfig-bgp-policy-ext:action: 'DENY' + match-set-options: 'ALL' + +overridden_01: + module_args: + config: + - name: test + type: standard + permit: True + local_as: True + no_export: True + no_peer: True + - name: test1 + no_advertise: True + permit: true + type: standard + state: overridden + existing_bgp_config: - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test1' + config: + community-set-name: 'test1' + community-member: + - NO_ADVERTISE + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' + - community-set-name: 'test3' + config: + community-set-name: 'test3' + community-member: + - NO_ADVERTISE + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - REGEX:808.* + - REGEX:919.* + - REGEX:700.* + - REGEX:888.* + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + response: + code: 200 + value: + sonic-vrf:VRF_LIST: + - vrf_name: default + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets/community-set=test3" method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/community-sets" + method: "patch" + data: + openconfig-bgp-policy:community-sets: + community-set: + - community-set-name: 'test' + config: + community-set-name: 'test' + community-member: + - NO_EXPORT_SUBCONFED + - NO_EXPORT + - NOPEER + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' diff --git a/tests/unit/modules/network/sonic/fixtures/sonic_bgp_ext_communities.yaml b/tests/unit/modules/network/sonic/fixtures/sonic_bgp_ext_communities.yaml index a7dcbec63..d860c4900 100644 --- a/tests/unit/modules/network/sonic/fixtures/sonic_bgp_ext_communities.yaml +++ b/tests/unit/modules/network/sonic/fixtures/sonic_bgp_ext_communities.yaml @@ -5,24 +5,12 @@ merged_01: - name: test1 members: regex: - - 808.* + - 808 match: all permit: True type: expanded + state: merged existing_bgp_config: - - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" - response: - code: 200 - value: - openconfig-network-instance:global: - config: - as: 5 - - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" - response: - code: 200 - value: - sonic-vrf:VRF_LIST: - - vrf_name: default - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" response: code: 200 @@ -36,7 +24,7 @@ merged_01: config: ext-community-set-name: 'test1' ext-community-member: - - REGEX:808.* + - REGEX:808 openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' merged_02: @@ -44,28 +32,43 @@ merged_02: config: - name: test1 members: - route_origin: - - 808.* + route_origin: + - "500:500" + type: standard + permit: true match: all + - name: test_ext + members: + regex: + - 800 + match: any permit: True type: expanded + state: merged existing_bgp_config: - - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" - response: - code: 200 - value: - openconfig-network-instance:global: - config: - as: 5 - - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" - response: - code: 200 - value: - sonic-vrf:VRF_LIST: - - vrf_name: default - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" response: code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test1' + config: + ext-community-set-name: 'test1' + ext-community-member: + - "route-origin:200:200" + - "route-target:400:400" + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - ext-community-set-name: 'test_ext' + config: + ext-community-set-name: 'test_ext' + ext-community-member: + - "REGEX:808" + - "REGEX:608" + - "REGEX:908" + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' expected_config_requests: - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" method: "patch" @@ -76,33 +79,33 @@ merged_02: config: ext-community-set-name: 'test1' ext-community-member: - - route-origin:808.* + - route-origin:500:500 openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test_ext' + config: + ext-community-set-name: 'test_ext' + ext-community-member: + - REGEX:800 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ANY' merged_03: module_args: config: - name: test1 members: route_target: - - 808.* + - "2.2.2.2:201" match: all permit: True - type: expanded + type: standard + state: merged existing_bgp_config: - - path: "/data/openconfig-network-instance:network-instances/network-instance=default/protocols/protocol=BGP,bgp/bgp/global" - response: - code: 200 - value: - openconfig-network-instance:global: - config: - as: 5 - - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" - response: - code: 200 - value: - sonic-vrf:VRF_LIST: - - vrf_name: default - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" response: code: 200 @@ -116,16 +119,18 @@ merged_03: config: ext-community-set-name: 'test1' ext-community-member: - - route-target:808.* + - route-target:2.2.2.2:201 openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' deleted_01: module_args: config: - name: test1 + type: expanded + match: all members: regex: - - 808.* + - 808 permit: true state: deleted existing_bgp_config: @@ -139,22 +144,17 @@ deleted_01: config: ext-community-set-name: 'test1' ext-community-member: - - REGEX:808.* + - REGEX:808 openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' - - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" - response: - code: 200 - value: - sonic-vrf:VRF_LIST: - - vrf_name: default expected_config_requests: - - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test1/config/ext-community-member=REGEX%3A808.%2A" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test1" method: "delete" deleted_02: module_args: config: - - name: test1 + - name: test2 + type: expanded members: regex: state: deleted @@ -165,21 +165,15 @@ deleted_02: value: openconfig-bgp-policy:ext-community-sets: ext-community-set: - - ext-community-set-name: 'test1' + - ext-community-set-name: 'test2' config: - ext-community-set-name: 'test1' + ext-community-set-name: 'test2' ext-community-member: - - REGEX:808.* + - REGEX:808 openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' - - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" - response: - code: 200 - value: - sonic-vrf:VRF_LIST: - - vrf_name: default expected_config_requests: - - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test1/config/ext-community-member" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test2" method: "delete" deleted_03: module_args: @@ -192,30 +186,25 @@ deleted_03: value: openconfig-bgp-policy:ext-community-sets: ext-community-set: - - ext-community-set-name: 'test1' + - ext-community-set-name: 'test3' config: - ext-community-set-name: 'test1' + ext-community-set-name: 'test3' ext-community-member: - - REGEX:808.* + - REGEX:710 openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' - - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" - response: - code: 200 - value: - sonic-vrf:VRF_LIST: - - vrf_name: default expected_config_requests: - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" method: "delete" deleted_04: module_args: config: - - name: test1 + - name: test4 members: route_origin: - - 808.* + - 600:600 permit: true + match: all state: deleted existing_bgp_config: - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" @@ -224,28 +213,21 @@ deleted_04: value: openconfig-bgp-policy:ext-community-sets: ext-community-set: - - ext-community-set-name: 'test1' + - ext-community-set-name: 'test4' config: - ext-community-set-name: 'test1' + ext-community-set-name: 'test4' ext-community-member: - - route-origin:808.* + - route-origin:600:600 openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' - - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" - response: - code: 200 - value: - sonic-vrf:VRF_LIST: - - vrf_name: default expected_config_requests: - - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test1/config/ext-community-member=route-origin%3A808.%2A" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test4" method: "delete" deleted_05: module_args: config: - - name: test1 + - name: test5 members: - regex: state: deleted existing_bgp_config: - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" @@ -254,21 +236,15 @@ deleted_05: value: openconfig-bgp-policy:ext-community-sets: ext-community-set: - - ext-community-set-name: 'test1' + - ext-community-set-name: 'test5' config: - ext-community-set-name: 'test1' + ext-community-set-name: 'test5' ext-community-member: - - route-origin:808.* + - route-origin:4403:301 openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' - - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" - response: - code: 200 - value: - sonic-vrf:VRF_LIST: - - vrf_name: default expected_config_requests: - - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test1/config/ext-community-member" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test5" method: "delete" deleted_06: module_args: @@ -281,29 +257,23 @@ deleted_06: value: openconfig-bgp-policy:ext-community-sets: ext-community-set: - - ext-community-set-name: 'test1' + - ext-community-set-name: 'test6' config: - ext-community-set-name: 'test1' + ext-community-set-name: 'test6' ext-community-member: - - route-origin:808.* + - route-origin:3303:201 openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' - - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" - response: - code: 200 - value: - sonic-vrf:VRF_LIST: - - vrf_name: default expected_config_requests: - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" method: "delete" deleted_07: module_args: config: - - name: test1 + - name: test7 members: route_target: - - 808.* + - 1.1.1.1:33 permit: true state: deleted existing_bgp_config: @@ -313,28 +283,23 @@ deleted_07: value: openconfig-bgp-policy:ext-community-sets: ext-community-set: - - ext-community-set-name: 'test1' + - ext-community-set-name: 'test7' config: - ext-community-set-name: 'test1' + ext-community-set-name: 'test7' ext-community-member: - - route-target:808.* + - route-target:1.1.1.1:33 + - route-target:2.2.2.2:33 openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' - - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" - response: - code: 200 - value: - sonic-vrf:VRF_LIST: - - vrf_name: default expected_config_requests: - - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test1/config/ext-community-member=route-target%3A808.%2A" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test7/config/ext-community-member=route-target%3A1.1.1.1%3A33" method: "delete" deleted_08: module_args: config: - - name: test1 + - name: test8 members: - regex: + route_target: state: deleted existing_bgp_config: - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" @@ -343,21 +308,15 @@ deleted_08: value: openconfig-bgp-policy:ext-community-sets: ext-community-set: - - ext-community-set-name: 'test1' + - ext-community-set-name: 'test8' config: - ext-community-set-name: 'test1' + ext-community-set-name: 'test8' ext-community-member: - - route-target:808.* + - route-target:2.2.2.2:33 openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' - - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" - response: - code: 200 - value: - sonic-vrf:VRF_LIST: - - vrf_name: default expected_config_requests: - - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test1/config/ext-community-member" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test8" method: "delete" deleted_09: module_args: @@ -370,19 +329,171 @@ deleted_09: value: openconfig-bgp-policy:ext-community-sets: ext-community-set: - - ext-community-set-name: 'test1' + - ext-community-set-name: 'test9' config: - ext-community-set-name: 'test1' + ext-community-set-name: 'test9' ext-community-member: - - route-target:808.* + - route-target:30.30.30.1:12 openconfig-bgp-policy-ext:action: 'PERMIT' match-set-options: 'ALL' - - path: "data/sonic-vrf:sonic-vrf/VRF/VRF_LIST" + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "delete" + +replaced_01: + module_args: + config: + - name: replace_test1 + members: + regex: + - 919 + match: all + permit: True + type: expanded + - name: replace_test3 + members: + route_origin: + - "101:101" + match: any + permit: False + type: standard + state: replaced + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" response: code: 200 value: - sonic-vrf:VRF_LIST: - - vrf_name: default + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'replace_test2' + config: + ext-community-set-name: 'replace_test2' + ext-community-member: + - REGEX:808 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - ext-community-set-name: 'replace_test1' + config: + ext-community-set-name: 'replace_test1' + ext-community-member: + - route-target:120.1.1.1:32 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - ext-community-set-name: 'replace_test3' + config: + ext-community-set-name: 'replace_test3' + ext-community-member: + - route-target:808:808 + - route-origin:101:101 + - route-origin:201:201 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=replace_test1" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=replace_test3" + method: "delete" - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'replace_test1' + config: + ext-community-set-name: 'replace_test1' + ext-community-member: + - REGEX:919 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'replace_test3' + config: + ext-community-set-name: 'replace_test3' + ext-community-member: + - route-origin:101:101 + openconfig-bgp-policy-ext:action: 'DENY' + match-set-options: 'ANY' + +overridden_01: + module_args: + config: + - name: test1 + members: + regex: + - 919 + match: all + permit: True + type: expanded + - name: test4 + members: + route_origin: + - "101:101" + match: any + permit: False + type: standard + state: overridden + existing_bgp_config: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + response: + code: 200 + value: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test2' + config: + ext-community-set-name: 'test2' + ext-community-member: + - REGEX:808 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - ext-community-set-name: 'test1' + config: + ext-community-set-name: 'test1' + ext-community-member: + - route-target:2.2.2.2:11 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - ext-community-set-name: 'test3' + config: + ext-community-set-name: 'test3' + ext-community-member: + - route-target:808:301 + - route-origin:101:101 + - route-origin:201:201 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + expected_config_requests: + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test1" method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test2" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets/ext-community-set=test3" + method: "delete" + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test1' + config: + ext-community-set-name: 'test1' + ext-community-member: + - REGEX:919 + openconfig-bgp-policy-ext:action: 'PERMIT' + match-set-options: 'ALL' + - path: "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/ext-community-sets" + method: "patch" + data: + openconfig-bgp-policy:ext-community-sets: + ext-community-set: + - ext-community-set-name: 'test4' + config: + ext-community-set-name: 'test4' + ext-community-member: + - route-origin:101:101 + openconfig-bgp-policy-ext:action: 'DENY' + match-set-options: 'ANY' diff --git a/tests/unit/modules/network/sonic/fixtures/sonic_interfaces.yaml b/tests/unit/modules/network/sonic/fixtures/sonic_interfaces.yaml index ee9d53e80..38a1a49d5 100644 --- a/tests/unit/modules/network/sonic/fixtures/sonic_interfaces.yaml +++ b/tests/unit/modules/network/sonic/fixtures/sonic_interfaces.yaml @@ -45,33 +45,79 @@ deleted_01: value: openconfig-interfaces:interfaces: interface: - - name: Eth1/1 + - name: 'Eth1/1' config: enabled: false description: 'Test Desc for eth1/1' - mtu: 1600 - - name: Loopback1 + mtu: 8888 + - name: 'Loopback123' config: enabled: false - description: 'Test Desc for eth1/1' + description: 'Test Desc for Loopback123' expected_config_requests: - - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config" + - path: "data/openconfig-interfaces:interfaces/interface=Loopback123" + method: "delete" + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config/description" + method: "delete" + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config/mtu" + method: "delete" + +deleted_02: + module_args: + state: deleted + config: + - name: 'Eth1/1' + fec: FEC_DISABLED + auto_negotiate: false + speed: SPEED_100GB + advertised_speed: + - 100000 + - name: 'Loopback123' + existing_interfaces_config: + - path: "data/openconfig-interfaces:interfaces" + response: + code: 200 + value: + openconfig-interfaces:interfaces: + interface: + - name: 'Eth1/1' + config: + mtu: 6767 + openconfig-if-ethernet:ethernet: + config: + auto-negotiate: true + port-speed: openconfig-if-ethernet:SPEED_40GB + openconfig-if-ethernet-ext2:port-fec: FEC_FC + openconfig-if-ethernet-ext2:advertised-speed: '100000,40000' + - name: 'Loopback123' + config: + enabled: false + description: 'Test Desc for Loopback123' + + expected_config_requests: + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/openconfig-if-ethernet:ethernet/config/openconfig-if-ethernet-ext2:port-fec" method: "patch" data: - openconfig-interfaces:config: - enabled: true - description: '' - mtu: 9100 - - path: "data/openconfig-interfaces:interfaces/interface=Loopback1" + openconfig-if-ethernet-ext2:port-fec: FEC_DISABLED + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/openconfig-if-ethernet:ethernet/config/port-speed" method: "delete" + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/openconfig-if-ethernet:ethernet/config/auto-negotiate" + method: "delete" + - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/openconfig-if-ethernet:ethernet/config/openconfig-if-ethernet-ext2:advertised-speed" + method: "patch" data: + openconfig-if-ethernet-ext2:advertised-speed: '40000' + - path: "data/openconfig-interfaces:interfaces/interface=Loopback123" + method: "delete" + - path: "data/sonic-port:sonic-port/PORT/PORT_LIST=Eth1%2F1/valid_speeds" + method: "get" replaced_01: module_args: state: replaced config: - name: Eth1/1 - mtu: 1600 + mtu: 5555 existing_interfaces_config: - path: "data/openconfig-interfaces:interfaces" response: @@ -89,14 +135,16 @@ replaced_01: method: "patch" data: openconfig-interfaces:config: - enabled: true + enabled: false description: '' mtu: 9100 - path: "data/openconfig-interfaces:interfaces/interface=Eth1%2F1/config" method: "patch" data: openconfig-interfaces:config: - mtu: 1600 + mtu: 5555 + - path: "data/sonic-port:sonic-port/PORT/PORT_LIST=Eth1%2F1/valid_speeds" + method: "get" overridden_01: module_args: diff --git a/tests/unit/modules/network/sonic/test_sonic_bgp_communities.py b/tests/unit/modules/network/sonic/test_sonic_bgp_communities.py index 1d7db62e7..64451a890 100644 --- a/tests/unit/modules/network/sonic/test_sonic_bgp_communities.py +++ b/tests/unit/modules/network/sonic/test_sonic_bgp_communities.py @@ -14,7 +14,7 @@ from .sonic_module import TestSonicModule -class TestSonicBgpModule(TestSonicModule): +class TestSonicBgpCommunitiesModule(TestSonicModule): module = sonic_bgp_communities @classmethod @@ -31,7 +31,7 @@ def setUpClass(cls): cls.fixture_data = cls.load_fixtures('sonic_bgp_communities.yaml') def setUp(self): - super(TestSonicBgpModule, self).setUp() + super(TestSonicBgpCommunitiesModule, self).setUp() self.facts_edit_config = self.mock_facts_edit_config.start() self.config_edit_config = self.mock_config_edit_config.start() @@ -42,7 +42,7 @@ def setUp(self): self.utils_edit_config.side_effect = self.facts_side_effect def tearDown(self): - super(TestSonicBgpModule, self).tearDown() + super(TestSonicBgpCommunitiesModule, self).tearDown() self.mock_facts_edit_config.stop() self.mock_config_edit_config.stop() self.mock_utils_edit_config.stop() @@ -81,3 +81,24 @@ def test_sonic_bgp_communities_deleted_03(self): self.initialize_config_requests(self.fixture_data['deleted_03']['expected_config_requests']) result = self.execute_module(changed=True) self.validate_config_requests() + + def test_sonic_bgp_communities_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_communities_replaced_02(self): + set_module_args(self.fixture_data['replaced_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_02']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['replaced_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_communities_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/tests/unit/modules/network/sonic/test_sonic_bgp_ext_communities.py b/tests/unit/modules/network/sonic/test_sonic_bgp_ext_communities.py index e3d806951..983554455 100644 --- a/tests/unit/modules/network/sonic/test_sonic_bgp_ext_communities.py +++ b/tests/unit/modules/network/sonic/test_sonic_bgp_ext_communities.py @@ -14,7 +14,7 @@ from .sonic_module import TestSonicModule -class TestSonicBgpModule(TestSonicModule): +class TestSonicBgpExtCommunitiesModule(TestSonicModule): module = sonic_bgp_ext_communities @classmethod @@ -31,7 +31,7 @@ def setUpClass(cls): cls.fixture_data = cls.load_fixtures('sonic_bgp_ext_communities.yaml') def setUp(self): - super(TestSonicBgpModule, self).setUp() + super(TestSonicBgpExtCommunitiesModule, self).setUp() self.facts_edit_config = self.mock_facts_edit_config.start() self.config_edit_config = self.mock_config_edit_config.start() @@ -42,7 +42,7 @@ def setUp(self): self.utils_edit_config.side_effect = self.facts_side_effect def tearDown(self): - super(TestSonicBgpModule, self).tearDown() + super(TestSonicBgpExtCommunitiesModule, self).tearDown() self.mock_facts_edit_config.stop() self.mock_config_edit_config.stop() self.mock_utils_edit_config.stop() @@ -130,3 +130,17 @@ def test_sonic_bgp_ext_communities_deleted_09(self): self.initialize_config_requests(self.fixture_data['deleted_09']['expected_config_requests']) result = self.execute_module(changed=True) self.validate_config_requests() + + def test_sonic_bgp_ext_communities_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_bgp_ext_communities_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_bgp_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() diff --git a/tests/unit/modules/network/sonic/test_sonic_interfaces.py b/tests/unit/modules/network/sonic/test_sonic_interfaces.py index a6432121e..2206e1603 100644 --- a/tests/unit/modules/network/sonic/test_sonic_interfaces.py +++ b/tests/unit/modules/network/sonic/test_sonic_interfaces.py @@ -61,6 +61,13 @@ def test_sonic_interfaces_deleted_01(self): result = self.execute_module(changed=True) self.validate_config_requests() + def test_sonic_interfaces_deleted_02(self): + set_module_args(self.fixture_data['deleted_02']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['deleted_02']['existing_interfaces_config']) + self.initialize_config_requests(self.fixture_data['deleted_02']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + def test_sonic_interfaces_replaced_01(self): set_module_args(self.fixture_data['replaced_01']['module_args']) self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_interfaces_config'])