Skip to content

Commit

Permalink
bgp_as_paths - Add support for 'replaced' and 'overridden' states (#290)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArunSaravananBalachandran authored Sep 20, 2023
1 parent e829a9e commit 1e6d5cb
Show file tree
Hide file tree
Showing 7 changed files with 701 additions and 91 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
minor_changes:
- sonic_bgp_as_paths - Add support for replaced and overridden states (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/290).
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ def __init__(self, **kwargs):
'type': 'list'},
'name': {'required': True, 'type': 'str'}},
'type': 'list'},
'state': {'choices': ['merged', 'deleted'],
'state': {'choices': ['merged', 'deleted', 'replaced', 'overridden'],
'default': 'merged',
'type': 'str'}} # pylint: disable=C0301
133 changes: 119 additions & 14 deletions plugins/module_utils/network/sonic/config/bgp_as_paths/bgp_as_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
search_obj_in_list
)
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import (
Expand Down Expand Up @@ -119,38 +120,142 @@ def set_state(self, want, have):
commands = []
requests = []
state = self._module.params['state']
diff = get_diff(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)
elif state == 'merged':
diff = get_diff(want, have)
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
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
add_commands = []
del_commands = []
commands = []
return commands
requests = []

for cmd in want:
# Set action to deny if not specfied for as-path-list
if cmd.get('permit') is None:
cmd['permit'] = False

@staticmethod
def _state_overridden(**kwargs):
match = search_obj_in_list(cmd['name'], have, 'name')
# Replace existing as-path-list
if match:
# Delete entire as-path-list if no members are specified
if not cmd.get('members'):
del_commands.append(match)
requests.append(self.get_delete_single_as_path_request(cmd['name']))
else:
if cmd['permit'] != match['permit']:
# If action is changed, delete the entire as-path list
# and add the given configuration
del_commands.append(match)
requests.append(self.get_delete_single_as_path_request(cmd['name']))
add_commands.append(cmd)
requests.append(self.get_new_add_request(cmd))
else:
want_members_set = set(cmd['members'])
have_members_set = set(match['members'])
members_to_delete = list(have_members_set.difference(want_members_set))
members_to_add = list(want_members_set.difference(have_members_set))
if members_to_delete:
del_commands.append({'name': cmd['name'], 'permit': cmd['permit'], 'members': members_to_delete})
if len(members_to_delete) == len(match['members']):
requests.append(self.get_delete_single_as_path_request(cmd['name']))
else:
requests.append(self.get_delete_single_as_path_member_request(cmd['name'], members_to_delete))

if members_to_add:
add_commands.append({'name': cmd['name'], 'permit': cmd['permit'], 'members': members_to_add})
requests.append(self.get_new_add_request({'name': cmd['name'], 'permit': cmd['permit'], 'members': members_to_add}))
else:
if cmd.get('members'):
add_commands.append(cmd)
requests.append(self.get_new_add_request(cmd))

if del_commands:
commands = update_states(del_commands, 'deleted')

if add_commands:
commands.extend(update_states(add_commands, 'replaced'))

return commands, requests

def _state_overridden(self, want, have):
""" The command generator when state is overridden
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
add_commands = []
del_commands = []
commands = []
return commands
requests = []

# Delete as-path-lists that are not specified
for cfg in have:
if not search_obj_in_list(cfg['name'], want, 'name'):
del_commands.append(cfg)
requests.append(self.get_delete_single_as_path_request(cfg['name']))

for cmd in want:
# Set action to deny if not specfied for as-path-list
if cmd.get('permit') is None:
cmd['permit'] = False

match = search_obj_in_list(cmd['name'], have, 'name')
# Override existing as-path-list
if match:
# Delete entire as-path-list if no members are specified
if not cmd.get('members'):
del_commands.append(match)
requests.append(self.get_delete_single_as_path_request(cmd['name']))
else:
if cmd['permit'] != match['permit']:
# If action is changed, delete the entire as-path list
# and add the given configuration
del_commands.append(match)
requests.append(self.get_delete_single_as_path_request(cmd['name']))
add_commands.append(cmd)
requests.append(self.get_new_add_request(cmd))
else:
want_members_set = set(cmd['members'])
have_members_set = set(match['members'])
members_to_delete = list(have_members_set.difference(want_members_set))
members_to_add = list(want_members_set.difference(have_members_set))
if members_to_delete:
del_commands.append({'name': cmd['name'], 'permit': cmd['permit'], 'members': members_to_delete})
if len(members_to_delete) == len(match['members']):
requests.append(self.get_delete_single_as_path_request(cmd['name']))
else:
requests.append(self.get_delete_single_as_path_member_request(cmd['name'], members_to_delete))

if members_to_add:
add_commands.append({'name': cmd['name'], 'permit': cmd['permit'], 'members': members_to_add})
requests.append(self.get_new_add_request({'name': cmd['name'], 'permit': cmd['permit'], 'members': members_to_add}))
else:
if cmd.get('members'):
add_commands.append(cmd)
requests.append(self.get_new_add_request(cmd))

if del_commands:
commands = update_states(del_commands, 'deleted')

if add_commands:
commands.extend(update_states(add_commands, 'overridden'))

return commands, requests

def _state_merged(self, want, have, diff):
""" The command generator when state is merged
Expand Down Expand Up @@ -239,7 +344,7 @@ def get_delete_all_as_path_requests(self, commands):
requests.append(request)
return requests

def get_delete_single_as_path_member_requests(self, name, members):
def get_delete_single_as_path_member_request(self, name, members):
url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:"
url = url + "bgp-defined-sets/as-path-sets/as-path-set={name}/config/{members_param}"
method = "DELETE"
Expand All @@ -248,7 +353,7 @@ def get_delete_single_as_path_member_requests(self, name, members):
request = {"path": url.format(name=name, members_param=members_str), "method": method}
return request

def get_delete_single_as_path_requests(self, name):
def get_delete_single_as_path_request(self, name):
url = "data/openconfig-routing-policy:routing-policy/defined-sets/openconfig-bgp-policy:bgp-defined-sets/as-path-sets/as-path-set={}"
method = "DELETE"
request = {"path": url.format(name), "method": method}
Expand All @@ -270,11 +375,11 @@ def get_delete_as_path_requests(self, commands, have, is_delete_all):
del_members = set(match['members']).intersection(set(members))
if del_members:
if len(del_members) == len(match['members']):
requests.append(self.get_delete_single_as_path_requests(name))
requests.append(self.get_delete_single_as_path_request(name))
else:
requests.append(self.get_delete_single_as_path_member_requests(name, del_members))
requests.append(self.get_delete_single_as_path_member_request(name, del_members))
else:
requests.append(self.get_delete_single_as_path_requests(name))
requests.append(self.get_delete_single_as_path_request(name))

return requests

Expand Down
128 changes: 101 additions & 27 deletions plugins/modules/sonic_bgp_as_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
choices:
- merged
- deleted
- replaced
- overridden
default: merged
"""
EXAMPLES = """
Expand All @@ -84,14 +86,14 @@
# action: permit
# members: 808.*,909.*
- name: Delete BGP as path list
dellemc.enterprise_sonic.sonic_bgp_as_paths:
config:
- name: test
members:
- 909.*
permit: true
state: deleted
- name: Delete BGP as path list
dellemc.enterprise_sonic.sonic_bgp_as_paths:
config:
- name: test
members:
- 909.*
permit: true
state: deleted
# After state:
# ------------
Expand All @@ -115,12 +117,12 @@
# action: deny
# members: 608.*,709.*
- name: Deletes BGP as-path list
dellemc.enterprise_sonic.sonic_bgp_as_paths:
config:
- name: test
members:
state: deleted
- name: Deletes BGP as-path list
dellemc.enterprise_sonic.sonic_bgp_as_paths:
config:
- name: test
members:
state: deleted
# After state:
# ------------
Expand All @@ -141,10 +143,10 @@
# action: permit
# members: 808.*,909.*
- name: Deletes BGP as-path list
dellemc.enterprise_sonic.sonic_bgp_as_paths:
config:
state: deleted
- name: Deletes BGP as-path list
dellemc.enterprise_sonic.sonic_bgp_as_paths:
config:
state: deleted
# After state:
# ------------
Expand All @@ -159,16 +161,16 @@
# -------------
#
# show bgp as-path-access-list
# AS path list test:
# (No bgp as-path-access-list configuration present)
- name: Adds 909.* to test as-path list
dellemc.enterprise_sonic.sonic_bgp_as_paths:
config:
- name: test
members:
- 909.*
permit: true
state: merged
- name: Create a BGP as-path list
dellemc.enterprise_sonic.sonic_bgp_as_paths:
config:
- name: test
members:
- 909.*
permit: true
state: merged
# After state:
# ------------
Expand All @@ -179,6 +181,78 @@
# members: 909.*
# Using replaced
# Before state:
# -------------
#
# show bgp as-path-access-list
# AS path list test:
# action: permit
# members: 800.*,808.*
# AS path list test1:
# action: deny
# members: 500.*
- name: Replace device configuration of specified BGP as-path lists with provided configuration
dellemc.enterprise_sonic.sonic_bgp_as_paths:
config:
- name: test
members:
- 900.*
- 901.*
permit: true
- name: test1
- name: test2
members:
- 100.*
permit: true
state: replaced
# After state:
# ------------
#
# show bgp as-path-access-list
# AS path list test:
# action: permit
# members: 900.*,901.*
# AS path list test2:
# action: permit
# members: 100.*
# Using overridden
# Before state:
# -------------
#
# show bgp as-path-access-list
# AS path list test:
# action: permit
# members: 800.*,808.*
# AS path list test1:
# action: deny
# members: 500.*
- name: Override device configuration of all BGP as-path lists with provided configuration
dellemc.enterprise_sonic.sonic_bgp_as_paths:
config:
- name: test
members:
- 900.*
- 901.*
permit: true
state: overridden
# After state:
# ------------
#
# show bgp as-path-access-list
# AS path list test:
# action: permit
# members: 900.*,901.*
"""
RETURN = """
before:
Expand Down
Loading

0 comments on commit 1e6d5cb

Please sign in to comment.