Skip to content

Commit

Permalink
Add diff and check for l3_interfaces module (#328)
Browse files Browse the repository at this point in the history
* Add diff and check for l3_interfaces module

* Add fragment file

* Add some change

* Add doc
  • Loading branch information
mingjunzhang2019 authored Jan 26, 2024
1 parent 7bdb7e7 commit 6f6b7b2
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- sonic_l3_interfaces - Add playbook check and diff modes support for l3_interfaces module (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/328).
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,27 @@
get_diff,
update_states,
normalize_interface_name,
remove_empties_from_list
)
from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import (
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 = [
{"addresses": {"address": "", "secondary": ""}}
]
TEST_KEYS_formatted_diff = [
{"config": {"name": "", '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}},
{"addresses": {"address": "", "secondary": "", '__delete_op': __DELETE_CONFIG_IF_NO_SUBCONFIG}}
]

DELETE = "DELETE"
PATCH = "PATCH"
Expand Down Expand Up @@ -95,6 +106,35 @@ def execute_module(self):
if result['changed']:
result['after'] = changed_l3_interfaces_facts

new_config = changed_l3_interfaces_facts
old_config = existing_l3_interfaces_facts
if self._module.check_mode:
result.pop('after', None)

existing_l3_intf_facts = remove_empties_from_list(existing_l3_interfaces_facts)
cmmnds = remove_empties_from_list(commands)

old_config = self.remove_default_entries(existing_l3_intf_facts)
cmds = self.remove_default_entries(cmmnds)

new_config = get_new_config(cmds, old_config,
TEST_KEYS_formatted_diff)
new_config = remove_empties_from_list(new_config)
new_config = self.remove_default_entries(new_config)
result['after(generated)'] = new_config

if self._module._diff:
old_conf = remove_empties_from_list(old_config)
old_conf = self.remove_default_entries(old_conf)

new_conf = remove_empties_from_list(new_config)
new_conf = self.remove_default_entries(new_conf)

self.sort_lists_in_config(old_conf)
self.sort_lists_in_config(new_conf)
result['diff'] = get_formatted_config_diff(old_conf,
new_conf,
self._module._verbosity)
result['warnings'] = warnings
return result

Expand Down Expand Up @@ -165,10 +205,14 @@ def _state_overridden(self, want, have):
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = list()
ret_requests = list()
commands = list()
new_want = self.update_object(want)
new_have = self.remove_default_entries(have)
new_want = remove_empties_from_list(new_want)
new_want = self.remove_default_entries(new_want)
new_have = remove_empties_from_list(have)
new_have = self.remove_default_entries(new_have)
get_override_interfaces = self.get_interface_object_for_overridden(new_have)
diff = get_diff(get_override_interfaces, new_want, TEST_KEYS)
diff2 = get_diff(new_want, get_override_interfaces, TEST_KEYS)
Expand Down Expand Up @@ -220,14 +264,29 @@ def _state_deleted(self, want, have):
commands = update_states(commands, "deleted")
return commands, requests

def remove_default_entries(self, have):
new_have = list()
for obj in have:
if obj['ipv4']['addresses'] is not None or obj['ipv4']['anycast_addresses'] is not None:
new_have.append(obj)
elif obj['ipv6']['addresses'] is not None or obj['ipv6']['enabled']:
new_have.append(obj)
return new_have
def remove_default_entries(self, config):
new_config = list()
for obj in config:
new_obj = dict()
if obj.get('ipv4', None) and \
(obj['ipv4'].get('addresses', None) or
obj['ipv4'].get('anycast_addresses', None)):
new_obj['ipv4'] = obj['ipv4']
if obj.get('ipv6', None) and \
(obj['ipv6'].get('addresses', None) or
obj['ipv6'].get('enabled', None) is not None):
new_obj['ipv6'] = obj['ipv6']

if new_obj:
key_set = set(obj.keys())
key_set.discard('ipv4')
key_set.discard('ipv6')
for key in key_set:
new_obj[key] = obj[key]

new_config.append(new_obj)

return new_config

def get_interface_object_for_replaced(self, have, want):
objects = list()
Expand Down Expand Up @@ -260,10 +319,19 @@ def get_interface_object_for_overridden(self, have):
objects = list()
for obj in have:
if 'name' in obj and obj['name'] != "Management0":
ipv4_addresses = obj['ipv4']['addresses']
ipv6_addresses = obj['ipv6']['addresses']
anycast_addresses = obj['ipv4']['anycast_addresses']
ipv6_enable = obj['ipv6']['enabled']
if obj.get('ipv4', None):
ipv4_addresses = obj['ipv4'].get('addresses', None)
anycast_addresses = obj['ipv4'].get('anycast_addresses', None)
else:
ipv4_addresses = None
anycast_addresses = None

if obj.get('ipv6', None):
ipv6_addresses = obj['ipv6'].get('addresses', None)
ipv6_enable = obj['ipv6'].get('enabled', None)
else:
ipv6_addresses = None
ipv6_enable = None

if ipv4_addresses is not None or ipv6_addresses is not None:
objects.append(obj.copy())
Expand Down Expand Up @@ -572,3 +640,14 @@ def get_sub_interface_name(self, name):
def build_update_ipv6_enabled(self, ipv6_enabled):
payload = {'config': {'enabled': ipv6_enabled}}
return payload

def sort_lists_in_config(self, config):
if config:
config.sort(key=lambda x: x['name'])
for cfg in config:
if cfg.get('ipv4', None) and cfg['ipv4'].get('addresses', None):
cfg['ipv4']['addresses'].sort(key=lambda x: x['address'])
if cfg.get('ipv4', None) and cfg['ipv4'].get('anycast_addresses', None):
cfg['ipv4']['anycast_addresses'].sort()
if cfg.get('ipv6', None) and cfg['ipv6'].get('addresses', None):
cfg['ipv6']['addresses'].sort(key=lambda x: x['address'])
16 changes: 9 additions & 7 deletions plugins/module_utils/network/sonic/utils/formatted_diff_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,15 @@ def __KEY_MATCH_OP_DEFAULT(key_set, command, exist_conf):
common_dict_list_key_set = dict_list_cmd_key_set.intersection(dict_list_exist_key_set)

key_matched_cnt = 0
for key in common_trival_key_set.union(common_dict_list_key_set):
if command[key] == exist_conf[key]:
if key in key_set:
key_present_cnt = 0
common_keys = common_trival_key_set.union(common_dict_list_key_set)
for key in key_set:
if key in common_keys:
key_present_cnt += 1
if command[key] == exist_conf[key]:
key_matched_cnt += 1

key_matched = (key_matched_cnt == len(key_set))
key_matched = (key_matched_cnt == key_present_cnt)
return key_matched


Expand Down Expand Up @@ -529,9 +532,6 @@ def derive_config_from_deleted_cmd_dict(command, exist_conf, test_keys=None, key
if not_dict_item or dict_no_key_item:
break

if dict_no_key_item:
new_conf_list = e_list

if not_dict_item:
c_set = set(c_list)
e_set = set(e_list)
Expand All @@ -540,6 +540,8 @@ def derive_config_from_deleted_cmd_dict(command, exist_conf, test_keys=None, key
new_conf[key] = list(delete_set)
else:
new_conf[key] = []
elif dict_no_key_item:
new_conf[key] = e_list
elif new_conf_list:
new_conf[key].extend(new_conf_list)

Expand Down
7 changes: 7 additions & 0 deletions plugins/modules/sonic_l3_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,13 @@
sample: >
The configuration returned will always be in the same format
of the parameters above.
after(generated):
description: The generated configuration model invocation.
returned: when C(check_mode)
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
Expand Down

0 comments on commit 6f6b7b2

Please sign in to comment.