Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add diff and check for l3_interfaces module #328

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'])
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