Standardize eos resource modules (#61736)
* 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
This commit is contained in:
parent
200ed25648
commit
7917d4def7
23 changed files with 350 additions and 265 deletions
3
changelogs/fragments/61736-eos-normalize-interface.yaml
Normal file
3
changelogs/fragments/61736-eos-normalize-interface.yaml
Normal file
|
@ -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.
|
|
@ -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)
|
||||
|
@ -12,10 +13,10 @@ created
|
|||
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):
|
||||
|
@ -93,148 +94,143 @@ class Interfaces(ConfigBase):
|
|||
: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
|
||||
|
||||
: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()
|
||||
|
||||
add_config = dict_diff(extant, desired)
|
||||
del_config = dict_diff(desired, extant)
|
||||
|
||||
commands.extend(generate_commands(key, add_config, del_config))
|
||||
|
||||
return commands
|
||||
|
||||
@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()
|
||||
|
||||
add_config = dict_diff(extant, desired)
|
||||
del_config = dict_diff(desired, extant)
|
||||
|
||||
commands.extend(generate_commands(key, add_config, del_config))
|
||||
|
||||
return commands
|
||||
|
||||
@staticmethod
|
||||
def _state_merged(want, have):
|
||||
""" The command generator when state is merged
|
||||
|
||||
: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()
|
||||
|
||||
add_config = dict_diff(extant, desired)
|
||||
|
||||
commands.extend(generate_commands(key, add_config, {}))
|
||||
|
||||
return commands
|
||||
|
||||
@staticmethod
|
||||
def _state_deleted(want, have):
|
||||
""" The command generator when state is deleted
|
||||
|
||||
: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)
|
||||
|
||||
commands.extend(generate_commands(key, {}, del_config))
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
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] = {}
|
||||
|
||||
# Otherwise it's the same as replaced
|
||||
return state_replaced(want, have)
|
||||
|
||||
|
||||
def state_merged(want, have):
|
||||
""" The command generator when state is merged
|
||||
|
||||
: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'])
|
||||
|
||||
|
||||
def state_deleted(want, have):
|
||||
""" The command generator when state is deleted
|
||||
|
||||
: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'])
|
||||
|
||||
|
||||
def _compute_commands(want, have, replace=False, remove=False):
|
||||
replace_params = {}
|
||||
remove_params = {}
|
||||
for name, config in want.items():
|
||||
extant = have.get(name, {})
|
||||
|
||||
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)
|
||||
|
||||
returns = {}
|
||||
if replace:
|
||||
returns['replace'] = _replace_config(replace_params)
|
||||
if remove:
|
||||
returns['remove'] = _remove_config(remove_params)
|
||||
|
||||
return returns
|
||||
|
||||
|
||||
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
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
|
|
@ -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_l2_interfaces class
|
||||
It is in this file where the current configuration (as dict)
|
||||
|
@ -12,10 +13,10 @@ created
|
|||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.common.utils import to_list, 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 L2_interfaces(ConfigBase):
|
||||
|
@ -94,6 +95,8 @@ class L2_interfaces(ConfigBase):
|
|||
to the desired configuration
|
||||
"""
|
||||
state = self._module.params['state']
|
||||
want = param_list_to_dict(want)
|
||||
have = param_list_to_dict(have)
|
||||
if state == 'overridden':
|
||||
commands = self._state_overridden(want, have)
|
||||
elif state == 'deleted':
|
||||
|
@ -113,15 +116,20 @@ class L2_interfaces(ConfigBase):
|
|||
to the desired configuration
|
||||
"""
|
||||
commands = []
|
||||
for interface in want:
|
||||
for extant in have:
|
||||
if extant['name'] == interface['name']:
|
||||
break
|
||||
for key, desired in want.items():
|
||||
interface_name = normalize_interface(key)
|
||||
if interface_name in have:
|
||||
extant = have[interface_name]
|
||||
else:
|
||||
continue
|
||||
extant = dict()
|
||||
|
||||
intf_commands = set_interface(desired, extant)
|
||||
intf_commands.extend(clear_interface(desired, extant))
|
||||
|
||||
if intf_commands:
|
||||
commands.append("interface {0}".format(interface_name))
|
||||
commands.extend(intf_commands)
|
||||
|
||||
commands.extend(clear_interface(interface, extant))
|
||||
commands.extend(set_interface(interface, extant))
|
||||
return commands
|
||||
|
||||
@staticmethod
|
||||
|
@ -133,19 +141,19 @@ class L2_interfaces(ConfigBase):
|
|||
to the desired configuration
|
||||
"""
|
||||
commands = []
|
||||
for extant in have:
|
||||
for interface in want:
|
||||
if extant['name'] == interface['name']:
|
||||
break
|
||||
for key, extant in have.items():
|
||||
if key in want:
|
||||
desired = want[key]
|
||||
else:
|
||||
# We didn't find a matching desired state, which means we can
|
||||
# pretend we recieved an empty desired state.
|
||||
interface = dict(name=extant['name'])
|
||||
commands.extend(clear_interface(interface, extant))
|
||||
continue
|
||||
desired = dict()
|
||||
|
||||
intf_commands = set_interface(desired, extant)
|
||||
intf_commands.extend(clear_interface(desired, extant))
|
||||
|
||||
if intf_commands:
|
||||
commands.append("interface {0}".format(key))
|
||||
commands.extend(intf_commands)
|
||||
|
||||
commands.extend(clear_interface(interface, extant))
|
||||
commands.extend(set_interface(interface, extant))
|
||||
return commands
|
||||
|
||||
@staticmethod
|
||||
|
@ -157,14 +165,18 @@ class L2_interfaces(ConfigBase):
|
|||
the current configuration
|
||||
"""
|
||||
commands = []
|
||||
for interface in want:
|
||||
for extant in have:
|
||||
if extant['name'] == interface['name']:
|
||||
break
|
||||
for key, desired in want.items():
|
||||
interface_name = normalize_interface(key)
|
||||
if interface_name in have:
|
||||
extant = have[interface_name]
|
||||
else:
|
||||
continue
|
||||
extant = dict()
|
||||
|
||||
commands.extend(set_interface(interface, extant))
|
||||
intf_commands = set_interface(desired, extant)
|
||||
|
||||
if intf_commands:
|
||||
commands.append("interface {0}".format(interface_name))
|
||||
commands.extend(intf_commands)
|
||||
|
||||
return commands
|
||||
|
||||
|
@ -177,29 +189,31 @@ class L2_interfaces(ConfigBase):
|
|||
of the provided objects
|
||||
"""
|
||||
commands = []
|
||||
for interface in want:
|
||||
for extant in have:
|
||||
if extant['name'] == interface['name']:
|
||||
break
|
||||
for key in want:
|
||||
desired = dict()
|
||||
if key in have:
|
||||
extant = have[key]
|
||||
else:
|
||||
continue
|
||||
|
||||
# Use an empty configuration, just in case
|
||||
interface = dict(name=interface['name'])
|
||||
commands.extend(clear_interface(interface, extant))
|
||||
intf_commands = clear_interface(desired, extant)
|
||||
|
||||
if intf_commands:
|
||||
commands.append("interface {0}".format(key))
|
||||
commands.extend(intf_commands)
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
def set_interface(want, have):
|
||||
commands = []
|
||||
wants_access = want["access"]
|
||||
wants_access = want.get("access")
|
||||
if wants_access:
|
||||
access_vlan = wants_access.get("vlan")
|
||||
if access_vlan and access_vlan != have.get("access", {}).get("vlan"):
|
||||
commands.append("switchport access vlan {0}".format(access_vlan))
|
||||
|
||||
wants_trunk = want["trunk"]
|
||||
wants_trunk = want.get("trunk")
|
||||
if wants_trunk:
|
||||
has_trunk = have.get("trunk", {})
|
||||
native_vlan = wants_trunk.get("native_vlan")
|
||||
|
@ -210,9 +224,6 @@ def set_interface(want, have):
|
|||
if allowed_vlans:
|
||||
allowed_vlans = ','.join(allowed_vlans)
|
||||
commands.append("switchport trunk allowed vlan {0}".format(allowed_vlans))
|
||||
|
||||
if commands:
|
||||
commands.insert(0, "interface {0}".format(want['name']))
|
||||
return commands
|
||||
|
||||
|
||||
|
@ -227,7 +238,4 @@ def clear_interface(want, have):
|
|||
commands.append("no switchport trunk allowed vlan")
|
||||
if "native_vlan" in has_trunk and "native_vlan" not in wants_trunk:
|
||||
commands.append("no switchport trunk native vlan")
|
||||
|
||||
if commands:
|
||||
commands.insert(0, "interface {0}".format(want["name"]))
|
||||
return commands
|
||||
|
|
|
@ -13,10 +13,10 @@ created
|
|||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.common.utils import to_list, 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 L3_interfaces(ConfigBase):
|
||||
|
@ -95,6 +95,8 @@ class L3_interfaces(ConfigBase):
|
|||
to the desired configuration
|
||||
"""
|
||||
state = self._module.params['state']
|
||||
want = param_list_to_dict(want)
|
||||
have = param_list_to_dict(have)
|
||||
if state == 'overridden':
|
||||
commands = self._state_overridden(want, have)
|
||||
elif state == 'deleted':
|
||||
|
@ -114,18 +116,18 @@ class L3_interfaces(ConfigBase):
|
|||
to the desired configuration
|
||||
"""
|
||||
commands = []
|
||||
for interface in want:
|
||||
for extant in have:
|
||||
if interface["name"] == extant["name"]:
|
||||
break
|
||||
for key, desired in want.items():
|
||||
interface_name = normalize_interface(key)
|
||||
if interface_name in have:
|
||||
extant = have[interface_name]
|
||||
else:
|
||||
extant = dict(name=interface["name"])
|
||||
extant = dict()
|
||||
|
||||
intf_commands = set_interface(interface, extant)
|
||||
intf_commands.extend(clear_interface(interface, extant))
|
||||
intf_commands = set_interface(desired, extant)
|
||||
intf_commands.extend(clear_interface(desired, extant))
|
||||
|
||||
if intf_commands:
|
||||
commands.append("interface {0}".format(interface["name"]))
|
||||
commands.append("interface {0}".format(interface_name))
|
||||
commands.extend(intf_commands)
|
||||
|
||||
return commands
|
||||
|
@ -139,22 +141,21 @@ class L3_interfaces(ConfigBase):
|
|||
to the desired configuration
|
||||
"""
|
||||
commands = []
|
||||
for extant in have:
|
||||
for interface in want:
|
||||
if extant["name"] == interface["name"]:
|
||||
break
|
||||
for key, extant in have.items():
|
||||
if key in want:
|
||||
desired = want[key]
|
||||
else:
|
||||
interface = dict(name=extant["name"])
|
||||
if interface.get("ipv4"):
|
||||
for ipv4 in interface["ipv4"]:
|
||||
desired = dict()
|
||||
if desired.get("ipv4"):
|
||||
for ipv4 in desired["ipv4"]:
|
||||
if ipv4["secondary"] is None:
|
||||
del ipv4["secondary"]
|
||||
|
||||
intf_commands = set_interface(interface, extant)
|
||||
intf_commands.extend(clear_interface(interface, extant))
|
||||
intf_commands = set_interface(desired, extant)
|
||||
intf_commands.extend(clear_interface(desired, extant))
|
||||
|
||||
if intf_commands:
|
||||
commands.append("interface {0}".format(interface["name"]))
|
||||
commands.append("interface {0}".format(key))
|
||||
commands.extend(intf_commands)
|
||||
|
||||
return commands
|
||||
|
@ -168,17 +169,17 @@ class L3_interfaces(ConfigBase):
|
|||
the current configuration
|
||||
"""
|
||||
commands = []
|
||||
for interface in want:
|
||||
for extant in have:
|
||||
if extant["name"] == interface["name"]:
|
||||
break
|
||||
for key, desired in want.items():
|
||||
interface_name = normalize_interface(key)
|
||||
if interface_name in have:
|
||||
extant = have[interface_name]
|
||||
else:
|
||||
extant = dict(name=interface["name"])
|
||||
extant = dict()
|
||||
|
||||
intf_commands = set_interface(interface, extant)
|
||||
intf_commands = set_interface(desired, extant)
|
||||
|
||||
if intf_commands:
|
||||
commands.append("interface {0}".format(interface["name"]))
|
||||
commands.append("interface {0}".format(interface_name))
|
||||
commands.extend(intf_commands)
|
||||
|
||||
return commands
|
||||
|
@ -192,19 +193,17 @@ class L3_interfaces(ConfigBase):
|
|||
of the provided objects
|
||||
"""
|
||||
commands = []
|
||||
for interface in want:
|
||||
for extant in have:
|
||||
if extant["name"] == interface["name"]:
|
||||
break
|
||||
for key in want:
|
||||
desired = dict()
|
||||
if key in have:
|
||||
extant = have[key]
|
||||
else:
|
||||
continue
|
||||
|
||||
# Clearing all args, send empty dictionary
|
||||
interface = dict(name=interface["name"])
|
||||
intf_commands = clear_interface(interface, extant)
|
||||
intf_commands = clear_interface(desired, extant)
|
||||
|
||||
if intf_commands:
|
||||
commands.append("interface {0}".format(interface["name"]))
|
||||
commands.append("interface {0}".format(key))
|
||||
commands.extend(intf_commands)
|
||||
|
||||
return commands
|
||||
|
@ -244,7 +243,7 @@ def clear_interface(want, have):
|
|||
if not want_ipv4:
|
||||
commands.append("no ip address")
|
||||
else:
|
||||
for address in (have_ipv4 - want_ipv4):
|
||||
for address in have_ipv4 - want_ipv4:
|
||||
address = dict(address)
|
||||
if "secondary" not in address:
|
||||
address["secondary"] = False
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
|
@ -17,6 +16,7 @@ __metaclass__ = type
|
|||
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 Lacp_interfaces(ConfigBase):
|
||||
|
@ -33,9 +33,6 @@ class Lacp_interfaces(ConfigBase):
|
|||
'lacp_interfaces',
|
||||
]
|
||||
|
||||
def __init__(self, module):
|
||||
super(Lacp_interfaces, self).__init__(module)
|
||||
|
||||
def get_lacp_interfaces_facts(self):
|
||||
""" Get the 'facts' (the current configuration)
|
||||
|
||||
|
@ -120,8 +117,9 @@ class Lacp_interfaces(ConfigBase):
|
|||
"""
|
||||
commands = []
|
||||
for key, desired in want.items():
|
||||
if key in have:
|
||||
extant = have[key]
|
||||
interface_name = normalize_interface(key)
|
||||
if interface_name in have:
|
||||
extant = have[interface_name]
|
||||
else:
|
||||
extant = dict()
|
||||
|
||||
|
@ -164,8 +162,9 @@ class Lacp_interfaces(ConfigBase):
|
|||
"""
|
||||
commands = []
|
||||
for key, desired in want.items():
|
||||
if key in have:
|
||||
extant = have[key]
|
||||
interface_name = normalize_interface(key)
|
||||
if interface_name in have:
|
||||
extant = have[interface_name]
|
||||
else:
|
||||
extant = dict()
|
||||
|
||||
|
@ -184,12 +183,12 @@ class Lacp_interfaces(ConfigBase):
|
|||
of the provided objects
|
||||
"""
|
||||
commands = []
|
||||
for key in want.keys():
|
||||
for key in want:
|
||||
desired = dict()
|
||||
if key in have:
|
||||
extant = have[key]
|
||||
else:
|
||||
extant = dict()
|
||||
continue
|
||||
|
||||
del_config = dict_diff(desired, extant)
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ from ansible.module_utils.network.common.utils import to_list, dict_diff
|
|||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.eos.facts.facts import Facts
|
||||
from ansible.module_utils.network.eos.utils.utils import normalize_interface
|
||||
|
||||
|
||||
class Lag_interfaces(ConfigBase):
|
||||
|
@ -114,11 +115,12 @@ class Lag_interfaces(ConfigBase):
|
|||
"""
|
||||
commands = []
|
||||
for interface in want:
|
||||
interface_name = normalize_interface(interface["name"])
|
||||
for extant in have:
|
||||
if extant["name"] == interface["name"]:
|
||||
if extant["name"] == interface_name:
|
||||
break
|
||||
else:
|
||||
extant = dict(name=interface["name"])
|
||||
extant = dict(name=interface_name)
|
||||
|
||||
commands.extend(set_config(interface, extant))
|
||||
commands.extend(remove_config(interface, extant))
|
||||
|
@ -135,18 +137,19 @@ class Lag_interfaces(ConfigBase):
|
|||
commands = []
|
||||
for extant in have:
|
||||
for interface in want:
|
||||
if interface["name"] == extant["name"]:
|
||||
if normalize_interface(interface["name"]) == extant["name"]:
|
||||
break
|
||||
else:
|
||||
interface = dict(name=extant["name"])
|
||||
commands.extend(remove_config(interface, extant))
|
||||
|
||||
for interface in want:
|
||||
interface_name = normalize_interface(interface["name"])
|
||||
for extant in have:
|
||||
if extant["name"] == interface["name"]:
|
||||
if extant["name"] == interface_name:
|
||||
break
|
||||
else:
|
||||
extant = dict(name=interface["name"])
|
||||
extant = dict(name=interface_name)
|
||||
commands.extend(set_config(interface, extant))
|
||||
|
||||
return commands
|
||||
|
@ -160,11 +163,12 @@ class Lag_interfaces(ConfigBase):
|
|||
"""
|
||||
commands = []
|
||||
for interface in want:
|
||||
interface_name = normalize_interface(interface["name"])
|
||||
for extant in have:
|
||||
if extant["name"] == interface["name"]:
|
||||
if extant["name"] == interface_name:
|
||||
break
|
||||
else:
|
||||
extant = dict(name=interface["name"])
|
||||
extant = dict(name=interface_name)
|
||||
|
||||
commands.extend(set_config(interface, extant))
|
||||
|
||||
|
@ -179,14 +183,15 @@ class Lag_interfaces(ConfigBase):
|
|||
"""
|
||||
commands = []
|
||||
for interface in want:
|
||||
interface_name = normalize_interface(interface["name"])
|
||||
for extant in have:
|
||||
if extant["name"] == interface["name"]:
|
||||
if extant["name"] == interface_name:
|
||||
break
|
||||
else:
|
||||
continue
|
||||
extant = dict(name=interface_name)
|
||||
|
||||
# Clearing all args, send empty dictionary
|
||||
interface = dict(name=interface["name"])
|
||||
interface = dict(name=interface_name)
|
||||
commands.extend(remove_config(interface, extant))
|
||||
|
||||
return commands
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
|
@ -17,6 +16,7 @@ __metaclass__ = type
|
|||
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 Lldp_interfaces(ConfigBase):
|
||||
|
@ -117,15 +117,16 @@ class Lldp_interfaces(ConfigBase):
|
|||
"""
|
||||
commands = []
|
||||
for key, desired in want.items():
|
||||
if key in have:
|
||||
extant = have[key]
|
||||
interface_name = normalize_interface(key)
|
||||
if interface_name in have:
|
||||
extant = have[interface_name]
|
||||
else:
|
||||
extant = dict(name=key)
|
||||
extant = dict(name=interface_name)
|
||||
|
||||
add_config = dict_diff(extant, desired)
|
||||
del_config = dict_diff(desired, extant)
|
||||
|
||||
commands.extend(generate_commands(key, add_config, del_config))
|
||||
commands.extend(generate_commands(interface_name, add_config, del_config))
|
||||
|
||||
return commands
|
||||
|
||||
|
@ -161,14 +162,15 @@ class Lldp_interfaces(ConfigBase):
|
|||
"""
|
||||
commands = []
|
||||
for key, desired in want.items():
|
||||
if key in have:
|
||||
extant = have[key]
|
||||
interface_name = normalize_interface(key)
|
||||
if interface_name in have:
|
||||
extant = have[interface_name]
|
||||
else:
|
||||
extant = dict(name=key)
|
||||
extant = dict(name=interface_name)
|
||||
|
||||
add_config = dict_diff(extant, desired)
|
||||
|
||||
commands.extend(generate_commands(key, add_config, {}))
|
||||
commands.extend(generate_commands(interface_name, add_config, {}))
|
||||
|
||||
return commands
|
||||
|
||||
|
@ -182,15 +184,16 @@ class Lldp_interfaces(ConfigBase):
|
|||
"""
|
||||
commands = []
|
||||
for key in want.keys():
|
||||
desired = dict(name=key)
|
||||
if key in have:
|
||||
extant = have[key]
|
||||
interface_name = normalize_interface(key)
|
||||
desired = dict(name=interface_name)
|
||||
if interface_name in have:
|
||||
extant = have[interface_name]
|
||||
else:
|
||||
extant = dict(name=key)
|
||||
continue
|
||||
|
||||
del_config = dict_diff(desired, extant)
|
||||
|
||||
commands.extend(generate_commands(key, {}, del_config))
|
||||
commands.extend(generate_commands(interface_name, {}, del_config))
|
||||
|
||||
return commands
|
||||
|
||||
|
|
|
@ -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 fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
|
@ -40,6 +41,7 @@ class InterfacesFacts(object):
|
|||
""" Populate the facts for interfaces
|
||||
|
||||
:param connection: the device connection
|
||||
:param ansible_facts: Facts dictionary
|
||||
:param data: previously collected configuration
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
|
@ -55,9 +57,8 @@ class InterfacesFacts(object):
|
|||
obj = self.render_config(self.generated_spec, conf)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
facts = {}
|
||||
facts = {'interfaces': []}
|
||||
if objs:
|
||||
facts['interfaces'] = []
|
||||
params = utils.validate_config(self.argument_spec, {'config': objs})
|
||||
for cfg in params['config']:
|
||||
facts['interfaces'].append(utils.remove_empties(cfg))
|
||||
|
|
|
@ -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 l2_interfaces fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
|
@ -39,9 +40,9 @@ class L2_interfacesFacts(object):
|
|||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for l2_interfaces
|
||||
|
||||
:param module: the module instance
|
||||
:param connection: the device connection
|
||||
:param data: previously collected conf
|
||||
:param ansible_facts: Facts dictionary
|
||||
:param data: previously collected configuration
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
|
|
|
@ -40,11 +40,11 @@ class L3_interfacesFacts(object):
|
|||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for l3_interfaces
|
||||
:param connection: the device connection
|
||||
:param ansible_facts: Facts dictionary
|
||||
:param data: previously collected configuration
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
|
||||
if not data:
|
||||
data = connection.get('show running-config | section ^interface')
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ class LacpFacts(object):
|
|||
""" Populate the facts for lacp
|
||||
:param connection: the device connection
|
||||
:param ansible_facts: Facts dictionary
|
||||
:param data: previously collected conf
|
||||
:param data: previously collected configuration
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
|
@ -42,7 +41,7 @@ class Lacp_interfacesFacts(object):
|
|||
""" Populate the facts for lacp_interfaces
|
||||
:param connection: the device connection
|
||||
:param ansible_facts: Facts dictionary
|
||||
:param data: previously collected conf
|
||||
:param data: previously collected configuration
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
|
|
|
@ -40,6 +40,7 @@ class Lag_interfacesFacts(object):
|
|||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for lag_interfaces
|
||||
:param connection: the device connection
|
||||
:param ansible_facts: Facts dictionary
|
||||
:param data: previously collected configuration
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
|
@ -69,7 +70,7 @@ class Lag_interfacesFacts(object):
|
|||
else:
|
||||
objs[group_name] = obj
|
||||
objs = list(objs.values())
|
||||
facts = {}
|
||||
facts = {'lag_interfaces': []}
|
||||
if objs:
|
||||
params = utils.validate_config(self.argument_spec, {'config': objs})
|
||||
facts['lag_interfaces'] = [utils.remove_empties(cfg) for cfg in params['config']]
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
|
@ -42,7 +41,7 @@ class Lldp_interfacesFacts(object):
|
|||
""" Populate the facts for lldp_interfaces
|
||||
:param connection: the device connection
|
||||
:param ansible_facts: Facts dictionary
|
||||
:param data: previously collected conf
|
||||
:param data: previously collected configuration
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
|
|
0
lib/ansible/module_utils/network/eos/utils/__init__.py
Normal file
0
lib/ansible/module_utils/network/eos/utils/__init__.py
Normal file
54
lib/ansible/module_utils/network/eos/utils/utils.py
Normal file
54
lib/ansible/module_utils/network/eos/utils/utils.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
# -*- 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)
|
||||
|
||||
# utils
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
def get_interface_number(name):
|
||||
digits = ''
|
||||
for char in name:
|
||||
if char.isdigit() or char in '/.':
|
||||
digits += char
|
||||
return digits
|
||||
|
||||
|
||||
def normalize_interface(name):
|
||||
"""Return the normalized interface name
|
||||
"""
|
||||
if not name:
|
||||
return None
|
||||
|
||||
if name.lower().startswith('et'):
|
||||
if_type = 'Ethernet'
|
||||
elif name.lower().startswith('lo'):
|
||||
if_type = 'Loopback'
|
||||
elif name.lower().startswith('ma'):
|
||||
if_type = 'Management'
|
||||
elif name.lower().startswith('po'):
|
||||
if_type = 'Port-Channel'
|
||||
elif name.lower().startswith('tu'):
|
||||
if_type = 'Tunnel'
|
||||
elif name.lower().startswith('vl'):
|
||||
if_type = 'Vlan'
|
||||
elif name.lower().startswith('vx'):
|
||||
if_type = 'Vxlan'
|
||||
else:
|
||||
if_type = None
|
||||
|
||||
number_list = name.split(' ')
|
||||
if len(number_list) == 2:
|
||||
number = number_list[-1].strip()
|
||||
else:
|
||||
number = get_interface_number(name)
|
||||
|
||||
if if_type:
|
||||
proper_interface = if_type + number
|
||||
else:
|
||||
proper_interface = name
|
||||
|
||||
return proper_interface
|
|
@ -1,7 +1,8 @@
|
|||
#!/usr/bin/python
|
||||
# -*- 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)
|
||||
|
||||
##############################################
|
||||
# WARNING #
|
||||
|
@ -41,6 +42,10 @@ version_added: 2.9
|
|||
short_description: Manages interface attributes of Arista EOS interfaces
|
||||
description: ['This module manages the interface attributes of Arista EOS interfaces.']
|
||||
author: ['Nathaniel Case (@qalthos)']
|
||||
notes:
|
||||
- Tested against Arista EOS 4.20.10M
|
||||
- This module works with connection C(network_cli). See the
|
||||
L(EOS Platform Options,../network/user_guide/platform_eos.html).
|
||||
options:
|
||||
config:
|
||||
description: The provided configuration
|
||||
|
|
|
@ -43,7 +43,7 @@ short_description: 'Manages L3 interface attributes of Arista EOS devices.'
|
|||
description: 'This module provides declarative management of Layer 3 interfaces on Arista EOS devices.'
|
||||
author: Nathaniel Case (@qalthos)
|
||||
notes:
|
||||
- 'Tested against vEOS v4.20.x'
|
||||
- Tested against Arista EOS 4.20.10M
|
||||
- This module works with connection C(network_cli). See the
|
||||
L(EOS Platform Options,../network/user_guide/platform_eos.html).
|
||||
options:
|
||||
|
|
|
@ -43,6 +43,10 @@ short_description: Manage Link Aggregation Control Protocol (LACP) attributes of
|
|||
description:
|
||||
- This module manages Link Aggregation Control Protocol (LACP) attributes of interfaces on Arista EOS devices.
|
||||
author: Nathaniel Case (@Qalthos)
|
||||
notes:
|
||||
- Tested against Arista EOS 4.20.10M
|
||||
- This module works with connection C(network_cli). See the
|
||||
L(EOS Platform Options,../network/user_guide/platform_eos.html).
|
||||
options:
|
||||
config:
|
||||
description: A dictionary of LACP interfaces options.
|
||||
|
|
|
@ -44,7 +44,7 @@ short_description: Manages link aggregation groups on Arista EOS devices
|
|||
description: This module manages attributes of link aggregation groups on Arista EOS devices.
|
||||
author: Nathaniel Case (@Qalthos)
|
||||
notes:
|
||||
- 'Tested against vEOS v4.20.x'
|
||||
- Tested against Arista EOS 4.20.10M
|
||||
- This module works with connection C(network_cli). See the
|
||||
L(EOS Platform Options,../network/user_guide/platform_eos.html).
|
||||
options:
|
||||
|
|
|
@ -43,6 +43,10 @@ short_description: Manage Global Link Layer Discovery Protocol (LLDP) settings o
|
|||
description:
|
||||
- This module manages Global Link Layer Discovery Protocol (LLDP) settings on Arista EOS devices.
|
||||
author: Nathaniel Case (@Qalthos)
|
||||
notes:
|
||||
- Tested against Arista EOS 4.20.10M
|
||||
- This module works with connection C(network_cli). See the
|
||||
L(EOS Platform Options,../network/user_guide/platform_eos.html).
|
||||
options:
|
||||
config:
|
||||
description: The provided global LLDP configuration.
|
||||
|
|
|
@ -43,6 +43,10 @@ short_description: Manage Link Layer Discovery Protocol (LLDP) attributes of int
|
|||
description:
|
||||
- This module manages Link Layer Discovery Protocol (LLDP) attributes of interfaces on Arista EOS devices.
|
||||
author: Nathaniel Case (@Qalthos)
|
||||
notes:
|
||||
- Tested against Arista EOS 4.20.10M
|
||||
- This module works with connection C(network_cli). See the
|
||||
L(EOS Platform Options,../network/user_guide/platform_eos.html).
|
||||
options:
|
||||
config:
|
||||
description: A dictionary of LLDP interfaces options.
|
||||
|
|
|
@ -26,4 +26,4 @@
|
|||
|
||||
- assert:
|
||||
that:
|
||||
- "'lag_interfaces' not in ansible_facts.network_resources"
|
||||
- "ansible_facts.network_resources.lag_interfaces == []"
|
||||
|
|
Loading…
Reference in a new issue