improve functionality for vlan trunks based on #4082

This commit is contained in:
Jason Edelman 2016-08-17 19:11:50 -04:00 committed by Matt Clay
parent 8da17b750d
commit 995083c0d9

View file

@ -27,40 +27,47 @@ description:
- Manages Layer 2 interfaces - Manages Layer 2 interfaces
author: Jason Edelman (@jedelman8) author: Jason Edelman (@jedelman8)
notes: notes:
- When C(state=absent), VLANs can be added/removed from trunk links and - When state=absent, vlans can be added/removed from trunk links and
the existing access VLAN can be 'unconfigured' to just having VLAN 1 the existing access vlan can be 'unconfigured' to just having VLAN 1
on that interface on that interface
- When working with trunks VLANs the keywords add/remove are always sent - When working with trunks VLANs the keywords add/remove are always sent
in the `switchport trunk allowed vlan` command. Use verbose mode to see in the `switchport trunk allowed vlan` command. Use verbose mode to see
commands sent. commands sent.
- When C(state=unconfigured), the interface will result with having a default - When state=unconfigured, the interface will result with having a default
Layer 2 interface, i.e. vlan 1 in access mode Layer 2 interface, i.e. vlan 1 in access mode
options: options:
interface: interface:
description: description:
- Full name of the interface, i.e. Ethernet1/1. - Full name of the interface, i.e. Ethernet1/1
required: true required: true
default: null default: null
mode: mode:
description: description:
- Mode for the Layer 2 port. - Mode for the Layer 2 port
required: false required: false
default: null default: null
choices: ['access','trunk'] choices: ['access','trunk']
access_vlan: access_vlan:
description: description:
- If C(mode=access), used as the access VLAN ID. - if mode=access, used as the access vlan id
required: false required: false
default: null default: null
native_vlan: native_vlan:
description: description:
- If C(mode=trunk), used as the trunk native VLAN ID. - if mode=trunk, used as the trunk native vlan id
required: false required: false
default: null default: null
trunk_vlans: trunk_vlans:
description: description:
- If C(mode=trunk), used as the VLAN range to ADD or REMOVE - if mode=trunk, used as the vlan range to ADD or REMOVE
from the trunk. from the trunk
required: false
aliases: trunk_add_vlans
default: null
trunk_allowed_vlans:
description:
- if mode=trunk, these are the only VLANs that should be
configured on the trunk
required: false required: false
default: null default: null
state: state:
@ -78,6 +85,9 @@ EXAMPLES = '''
# ENSURE Eth1/5 is configured for access vlan 20 # ENSURE Eth1/5 is configured for access vlan 20
- nxos_switchport: interface=eth1/5 mode=access access_vlan=20 host={{ inventory_hostname }} - nxos_switchport: interface=eth1/5 mode=access access_vlan=20 host={{ inventory_hostname }}
# ENSURE Eth1/5 only has vlans 5-10 as trunk vlans
- nxos_switchport: interface=eth1/5 mode=trunk native_vlan=10 trunk_vlans=5-10 host={{ inventory_hostname }}
# Ensure eth1/5 is a trunk port and ensure 2-50 are being tagged (doesn't mean others aren't also being tagged) # Ensure eth1/5 is a trunk port and ensure 2-50 are being tagged (doesn't mean others aren't also being tagged)
- nxos_switchport: interface=eth1/5 mode=trunk native_vlan=10 trunk_vlans=2-50 host={{ inventory_hostname }} - nxos_switchport: interface=eth1/5 mode=trunk native_vlan=10 trunk_vlans=2-50 host={{ inventory_hostname }}
@ -264,7 +274,7 @@ def get_switchport(port, module):
return {} return {}
def remove_switchport_config_commands(interface, existing, proposed): def remove_switchport_config_commands(interface, existing, proposed, module):
mode = proposed.get('mode') mode = proposed.get('mode')
commands = [] commands = []
command = None command = None
@ -277,14 +287,13 @@ def remove_switchport_config_commands(interface, existing, proposed):
elif mode == 'trunk': elif mode == 'trunk':
tv_check = existing.get('trunk_vlans_list') == proposed.get('trunk_vlans_list') tv_check = existing.get('trunk_vlans_list') == proposed.get('trunk_vlans_list')
if not tv_check: if not tv_check:
vlans_to_remove = False existing_vlans = existing.get('trunk_vlans_list')
for vlan in proposed.get('trunk_vlans_list'): proposed_vlans = proposed.get('trunk_vlans_list')
if vlan in existing.get('trunk_vlans_list'): vlans_to_remove = set(proposed_vlans).intersection(existing_vlans)
vlans_to_remove = True # module.exit_json(ex=existing, pr=proposed, vlans_to_remove=list(vlans_to_remove))
break
if vlans_to_remove: if vlans_to_remove:
command = 'switchport trunk allowed vlan remove {0}'.format( command = 'switchport trunk allowed vlan remove {0}'.format(
proposed.get('trunk_vlans')) proposed.get('trunk_vlans', proposed.get('trunk_allowed_vlans')))
commands.append(command) commands.append(command)
native_check = existing.get( native_check = existing.get(
'native_vlan') == proposed.get('native_vlan') 'native_vlan') == proposed.get('native_vlan')
@ -297,7 +306,7 @@ def remove_switchport_config_commands(interface, existing, proposed):
return commands return commands
def get_switchport_config_commands(interface, existing, proposed): def get_switchport_config_commands(interface, existing, proposed, module):
"""Gets commands required to config a given switchport interface """Gets commands required to config a given switchport interface
""" """
@ -323,14 +332,17 @@ def get_switchport_config_commands(interface, existing, proposed):
elif proposed_mode == 'trunk': elif proposed_mode == 'trunk':
tv_check = existing.get('trunk_vlans_list') == proposed.get('trunk_vlans_list') tv_check = existing.get('trunk_vlans_list') == proposed.get('trunk_vlans_list')
if not tv_check: if not tv_check:
vlans_to_add = False # module.exit_json(aaaa=existing, tvtv=tv_check, pr=proposed)
for vlan in proposed.get('trunk_vlans_list'): if proposed.get('allowed'):
if vlan not in existing.get('trunk_vlans_list'): command = 'switchport trunk allowed vlan {0}'.format(proposed.get('trunk_allowed_vlans'))
vlans_to_add = True
break
if vlans_to_add:
command = 'switchport trunk allowed vlan add {0}'.format(proposed.get('trunk_vlans'))
commands.append(command) commands.append(command)
else:
existing_vlans = existing.get('trunk_vlans_list')
proposed_vlans = proposed.get('trunk_vlans_list')
vlans_to_add = set(proposed_vlans).difference(existing_vlans)
if vlans_to_add:
command = 'switchport trunk allowed vlan add {0}'.format(proposed.get('trunk_vlans'))
commands.append(command)
native_check = existing.get( native_check = existing.get(
'native_vlan') == proposed.get('native_vlan') 'native_vlan') == proposed.get('native_vlan')
@ -440,8 +452,7 @@ def apply_value_map(value_map, resource):
def execute_config_command(commands, module): def execute_config_command(commands, module):
try: try:
module.configure(commands) module.configure(commands)
except ShellError: except ShellError, clie:
clie = get_exception()
module.fail_json(msg='Error sending CLI commands', module.fail_json(msg='Error sending CLI commands',
error=str(clie), commands=commands) error=str(clie), commands=commands)
@ -470,8 +481,7 @@ def execute_show(cmds, module, command_type=None):
response = module.execute(cmds, command_type=command_type) response = module.execute(cmds, command_type=command_type)
else: else:
response = module.execute(cmds) response = module.execute(cmds)
except ShellError: except ShellError, clie:
clie = get_exception()
module.fail_json(msg='Error sending {0}'.format(command), module.fail_json(msg='Error sending {0}'.format(command),
error=str(clie)) error=str(clie))
return response return response
@ -508,13 +518,15 @@ def main():
mode=dict(choices=['access', 'trunk'], required=False), mode=dict(choices=['access', 'trunk'], required=False),
access_vlan=dict(type='str', required=False), access_vlan=dict(type='str', required=False),
native_vlan=dict(type='str', required=False), native_vlan=dict(type='str', required=False),
trunk_vlans=dict(type='str', required=False), trunk_vlans=dict(type='str', aliases=['trunk_add_vlans'], required=False),
trunk_allowed_vlans=dict(type='str', required=False),
state=dict(choices=['absent', 'present', 'unconfigured'], state=dict(choices=['absent', 'present', 'unconfigured'],
default='present') default='present')
) )
module = get_module(argument_spec=argument_spec, module = get_module(argument_spec=argument_spec,
mutually_exclusive=[['access_vlan', 'trunk_vlans'], mutually_exclusive=[['access_vlan', 'trunk_vlans'],
['access_vlan', 'native_vlan']], ['access_vlan', 'native_vlan'],
['access_vlan', 'trunk_allowed_vlans']],
supports_check_mode=True) supports_check_mode=True)
interface = module.params['interface'] interface = module.params['interface']
@ -523,9 +535,11 @@ def main():
state = module.params['state'] state = module.params['state']
trunk_vlans = module.params['trunk_vlans'] trunk_vlans = module.params['trunk_vlans']
native_vlan = module.params['native_vlan'] native_vlan = module.params['native_vlan']
trunk_allowed_vlans = module.params['trunk_allowed_vlans']
args = dict(interface=interface, mode=mode, access_vlan=access_vlan, args = dict(interface=interface, mode=mode, access_vlan=access_vlan,
native_vlan=native_vlan, trunk_vlans=trunk_vlans) native_vlan=native_vlan, trunk_vlans=trunk_vlans,
trunk_allowed_vlans=trunk_allowed_vlans)
proposed = dict((k, v) for k, v in args.iteritems() if v is not None) proposed = dict((k, v) for k, v in args.iteritems() if v is not None)
@ -572,8 +586,12 @@ def main():
' on an interface that\ndoes not exist on the ' ' on an interface that\ndoes not exist on the '
' switch yet!', vlan=native_vlan) ' switch yet!', vlan=native_vlan)
if trunk_vlans: if trunk_vlans or trunk_allowed_vlans:
trunk_vlans_list = vlan_range_to_list(trunk_vlans) if trunk_vlans:
trunk_vlans_list = vlan_range_to_list(trunk_vlans)
elif trunk_allowed_vlans:
trunk_vlans_list = vlan_range_to_list(trunk_allowed_vlans)
proposed['allowed'] = True
existing_trunks_list = vlan_range_to_list( existing_trunks_list = vlan_range_to_list(
(existing['trunk_vlans']) (existing['trunk_vlans'])
@ -585,9 +603,9 @@ def main():
changed = False changed = False
commands = [] commands = []
if state == 'present': if state == 'present':
command = get_switchport_config_commands(interface, existing, proposed) # module.exit_json(ex=existing, pr=proposed)
command = get_switchport_config_commands(interface, existing, proposed, module)
commands.append(command) commands.append(command)
elif state == 'unconfigured': elif state == 'unconfigured':
is_default = is_switchport_default(existing) is_default = is_switchport_default(existing)
@ -595,11 +613,11 @@ def main():
command = default_switchport_config(interface) command = default_switchport_config(interface)
commands.append(command) commands.append(command)
elif state == 'absent': elif state == 'absent':
command = remove_switchport_config_commands(interface, command = remove_switchport_config_commands(interface, existing,
existing, proposed) proposed, module)
commands.append(command) commands.append(command)
if trunk_vlans: if trunk_vlans or trunk_allowed_vlans:
existing.pop('trunk_vlans_list') existing.pop('trunk_vlans_list')
proposed.pop('trunk_vlans_list') proposed.pop('trunk_vlans_list')