Skip to content

Commit

Permalink
Standardize eos resource modules (ansible#61736)
Browse files Browse the repository at this point in the history
* Fix eos_l3_interfaces case sensitivity

* Unify EOS module notes

* Add normalize_interfaces to eos_l2_interfaces

* Pull normalize_interface into eos_interfaces

* Add normalize_interface to lag_interfaces

* Add normalize_interface to lldp_interfaces

* Add normalize_interface to lacp_interfaces

* more module cleanup

* Add changelog
  • Loading branch information
Qalthos authored and vasilyprokopov committed Sep 15, 2019
1 parent 1e0506a commit 037bf28
Show file tree
Hide file tree
Showing 23 changed files with 330 additions and 245 deletions.
3 changes: 3 additions & 0 deletions changelogs/fragments/61736-eos-normalize-interface.yaml
@@ -0,0 +1,3 @@
bugfixes:
- Remove case sensitivity on interface names from eos_interfaces, eos_l2_interfaces, eos_l3_interfaces,
eos_lacp_interfaces, eos_lag_interfaces, and eos_lldp_interfaces.
228 changes: 112 additions & 116 deletions lib/ansible/module_utils/network/eos/config/interfaces/interfaces.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""
The eos_interfaces class
It is in this file where the current configuration (as dict)
Expand All @@ -12,10 +13,10 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

from ansible.module_utils.network.common.utils import to_list, param_list_to_dict

from ansible.module_utils.network.common.cfg.base import ConfigBase
from ansible.module_utils.network.common.utils import to_list, dict_diff, param_list_to_dict
from ansible.module_utils.network.eos.facts.facts import Facts
from ansible.module_utils.network.eos.utils.utils import normalize_interface


class Interfaces(ConfigBase):
Expand Down Expand Up @@ -93,148 +94,143 @@ def set_state(self, want, have):
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
state = self._module.params['state']
want = param_list_to_dict(want)
have = param_list_to_dict(have)
state = self._module.params['state']
if state == 'overridden':
commands = state_overridden(want, have)
commands = self._state_overridden(want, have)
elif state == 'deleted':
commands = state_deleted(want, have)
commands = self._state_deleted(want, have)
elif state == 'merged':
commands = state_merged(want, have)
commands = self._state_merged(want, have)
elif state == 'replaced':
commands = state_replaced(want, have)
commands = self._state_replaced(want, have)
return commands

@staticmethod
def _state_replaced(want, have):
""" The command generator when state is replaced
def state_replaced(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
"""
commands = _compute_commands(want, have, replace=True, remove=True)

replace = commands['replace']
remove = commands['remove']

commands_by_interface = replace
for interface, commands in remove.items():
commands_by_interface[interface] = replace.get(interface, []) + commands

return _flatten_commands(commands_by_interface)


def state_overridden(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 empty desired state for unspecified interfaces
for key in have:
if key not in want:
want[key] = {}
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
for key, desired in want.items():
interface_name = normalize_interface(key)
if interface_name in have:
extant = have[interface_name]
else:
extant = dict()

# Otherwise it's the same as replaced
return state_replaced(want, have)
add_config = dict_diff(extant, desired)
del_config = dict_diff(desired, extant)

commands.extend(generate_commands(key, add_config, del_config))

def state_merged(want, have):
""" The command generator when state is merged
return commands

:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = _compute_commands(want, have, replace=True)
return _flatten_commands(commands['replace'])
@staticmethod
def _state_overridden(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
"""
commands = []
for key, extant in have.items():
if key in want:
desired = want[key]
else:
desired = dict()

def state_deleted(want, have):
""" The command generator when state is deleted
add_config = dict_diff(extant, desired)
del_config = dict_diff(desired, extant)

:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = _compute_commands(want, have, remove=True)
return _flatten_commands(commands['remove'])
commands.extend(generate_commands(key, add_config, del_config))

return commands

def _compute_commands(want, have, replace=False, remove=False):
replace_params = {}
remove_params = {}
for name, config in want.items():
extant = have.get(name, {})
@staticmethod
def _state_merged(want, have):
""" The command generator when state is merged
if remove:
remove_params[name] = dict(set(extant.items()).difference(config.items()))
if replace:
replace_params[name] = dict(set(config.items()).difference(extant.items()))
if remove:
# We won't need to also clear the configuration if we've
# already set it to something
for param in replace_params[name]:
remove_params[name].pop(param, None)
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
commands = []
for key, desired in want.items():
interface_name = normalize_interface(key)
if interface_name in have:
extant = have[interface_name]
else:
extant = dict()

returns = {}
if replace:
returns['replace'] = _replace_config(replace_params)
if remove:
returns['remove'] = _remove_config(remove_params)
add_config = dict_diff(extant, desired)

return returns
commands.extend(generate_commands(key, add_config, {}))

return commands

def _remove_config(params):
"""
Generates commands to reset config to defaults based on keys provided.
"""
commands = {}
for interface, config in params.items():
interface_commands = []
for param in config:
if param == 'enabled':
interface_commands.append('no shutdown')
elif param in ('description', 'mtu'):
interface_commands.append('no {0}'.format(param))
elif param == 'speed':
interface_commands.append('speed auto')
if interface_commands:
commands[interface] = interface_commands
@staticmethod
def _state_deleted(want, have):
""" The command generator when state is deleted
return commands
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
for key in want:
desired = dict()
if key in have:
extant = have[key]
else:
continue

del_config = dict_diff(desired, extant)

def _replace_config(params):
"""
Generates commands to replace config to new values based on provided dictionary.
"""
commands = {}
for interface, config in params.items():
interface_commands = []
for param, state in config.items():
if param == 'description':
interface_commands.append('description "{0}"'.format(state))
elif param == 'enabled':
interface_commands.append('{0}shutdown'.format('no ' if state else ''))
elif param == 'mtu':
interface_commands.append('mtu {0}'.format(state))
if 'speed' in config:
interface_commands.append('speed {0}{1}'.format(config['speed'], config['duplex']))
if interface_commands:
commands[interface] = interface_commands
commands.extend(generate_commands(key, {}, del_config))

return commands
return commands


def _flatten_commands(command_dict):
def generate_commands(interface, to_set, to_remove):
commands = []
for interface, interface_commands in command_dict.items():
commands.append('interface {0}'.format(interface))
commands.extend(interface_commands)
for key, value in to_set.items():
if value is None:
continue

if key == "enabled":
commands.append('{0}shutdown'.format('no ' if value else ''))
elif key == "speed":
if value == "auto":
commands.append("{0} {1}".format(key, value))
else:
commands.append('speed {0}{1}'.format(value, to_set['duplex']))
elif key == "duplex":
# duplex is handled with speed
continue
else:
commands.append("{0} {1}".format(key, value))

# Don't try to also remove the same key, if present in to_remove
to_remove.pop(key, None)

for key in to_remove.keys():
if key == "enabled":
commands.append('no shutdown')
elif key == "speed":
commands.append("speed auto")
elif key == "duplex":
# duplex is handled with speed
continue
else:
commands.append("no {0}".format(key))

if commands:
commands.insert(0, "interface {0}".format(interface))

return commands

0 comments on commit 037bf28

Please sign in to comment.