Fix nxos_switchport and unit test (#26131)
* fix nxos_switchport Signed-off-by: Trishna Guha <trishnaguha17@gmail.com> * nxos_switchport unit test Signed-off-by: Trishna Guha <trishnaguha17@gmail.com> * legacy file * update unit test * handle exceptionpull/4420/head
parent
6af53cf0ef
commit
c5fb4bbcc0
|
@ -16,10 +16,10 @@
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
ANSIBLE_METADATA = {
|
||||||
'status': ['preview'],
|
'metadata_version': '1.0',
|
||||||
'supported_by': 'community'}
|
'status': ['preview'],
|
||||||
|
'supported_by': 'community'}
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
DOCUMENTATION = '''
|
||||||
---
|
---
|
||||||
|
@ -28,74 +28,73 @@ extends_documentation_fragment: nxos
|
||||||
version_added: "2.1"
|
version_added: "2.1"
|
||||||
short_description: Manages Layer 2 switchport interfaces.
|
short_description: Manages Layer 2 switchport interfaces.
|
||||||
description:
|
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 C(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 C(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 C(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 C(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 C(mode=trunk), used as the VLAN range to ADD or REMOVE
|
||||||
from the trunk.
|
from the trunk.
|
||||||
aliases:
|
aliases:
|
||||||
- trunk_add_vlans
|
- trunk_add_vlans
|
||||||
required: false
|
required: false
|
||||||
default: null
|
default: null
|
||||||
state:
|
state:
|
||||||
description:
|
description:
|
||||||
- Manage the state of the resource.
|
- Manage the state of the resource.
|
||||||
required: false
|
required: false
|
||||||
default: present
|
default: present
|
||||||
choices: ['present','absent', 'unconfigured']
|
choices: ['present','absent', 'unconfigured']
|
||||||
trunk_allowed_vlans:
|
trunk_allowed_vlans:
|
||||||
description:
|
description:
|
||||||
- if C(mode=trunk), these are the only VLANs that will be
|
- if C(mode=trunk), these are the only VLANs that will be
|
||||||
configured on the trunk, i.e. "2-10,15".
|
configured on the trunk, i.e. "2-10,15".
|
||||||
required: false
|
required: false
|
||||||
version_added: 2.2
|
version_added: 2.2
|
||||||
default: null
|
default: null
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
- name: Ensure Eth1/5 is in its default switchport state
|
- name: Ensure Eth1/5 is in its default switchport state
|
||||||
nxos_switchport:
|
nxos_switchport:
|
||||||
interface: eth1/5
|
interface: eth1/5
|
||||||
state: unconfigured
|
state: unconfigured
|
||||||
host: "{{ inventory_hostname }}"
|
|
||||||
|
|
||||||
- name: Ensure Eth1/5 is configured for access vlan 20
|
- name: Ensure Eth1/5 is configured for access vlan 20
|
||||||
nxos_switchport:
|
nxos_switchport:
|
||||||
interface: eth1/5
|
interface: eth1/5
|
||||||
mode: access
|
mode: access
|
||||||
access_vlan: 20
|
access_vlan: 20
|
||||||
host: "{{ inventory_hostname }}"
|
|
||||||
|
|
||||||
- name: Ensure Eth1/5 only has vlans 5-10 as trunk vlans
|
- name: Ensure Eth1/5 only has vlans 5-10 as trunk vlans
|
||||||
nxos_switchport:
|
nxos_switchport:
|
||||||
|
@ -103,7 +102,6 @@ EXAMPLES = '''
|
||||||
mode: trunk
|
mode: trunk
|
||||||
native_vlan: 10
|
native_vlan: 10
|
||||||
trunk_vlans: 5-10
|
trunk_vlans: 5-10
|
||||||
host: "{{ inventory_hostname }}"
|
|
||||||
|
|
||||||
- name: Ensure eth1/5 is a trunk port and ensure 2-50 are being tagged (doesn't mean others aren't also being tagged)
|
- name: 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:
|
nxos_switchport:
|
||||||
|
@ -111,59 +109,28 @@ EXAMPLES = '''
|
||||||
mode: trunk
|
mode: trunk
|
||||||
native_vlan: 10
|
native_vlan: 10
|
||||||
trunk_vlans: 2-50
|
trunk_vlans: 2-50
|
||||||
host: "{{ inventory_hostname }}"
|
|
||||||
|
|
||||||
- name: Ensure these VLANs are not being tagged on the trunk
|
- name: Ensure these VLANs are not being tagged on the trunk
|
||||||
nxos_switchport:
|
nxos_switchport:
|
||||||
interface: eth1/5
|
interface: eth1/5
|
||||||
mode: trunk
|
mode: trunk
|
||||||
trunk_vlans: 51-4094
|
trunk_vlans: 51-4094
|
||||||
host: "{{ inventory_hostname }} "
|
|
||||||
state: absent
|
state: absent
|
||||||
'''
|
'''
|
||||||
|
|
||||||
RETURN = '''
|
RETURN = '''
|
||||||
proposed:
|
commands:
|
||||||
description: k/v pairs of parameters passed into module
|
|
||||||
returned: always
|
|
||||||
type: dict
|
|
||||||
sample: {"access_vlan": "10", "interface": "eth1/5", "mode": "access"}
|
|
||||||
existing:
|
|
||||||
description: k/v pairs of existing switchport
|
|
||||||
returned: always
|
|
||||||
type: dict
|
|
||||||
sample: {"access_vlan": "10", "access_vlan_name": "VLAN0010",
|
|
||||||
"interface": "Ethernet1/5", "mode": "access",
|
|
||||||
"native_vlan": "1", "native_vlan_name": "default",
|
|
||||||
"switchport": "Enabled", "trunk_vlans": "1-4094"}
|
|
||||||
end_state:
|
|
||||||
description: k/v pairs of switchport after module execution
|
|
||||||
returned: always
|
|
||||||
type: dict
|
|
||||||
sample: {"access_vlan": "10", "access_vlan_name": "VLAN0010",
|
|
||||||
"interface": "Ethernet1/5", "mode": "access",
|
|
||||||
"native_vlan": "1", "native_vlan_name": "default",
|
|
||||||
"switchport": "Enabled", "trunk_vlans": "1-4094"}
|
|
||||||
updates:
|
|
||||||
description: command string sent to the device
|
description: command string sent to the device
|
||||||
returned: always
|
returned: always
|
||||||
type: list
|
type: list
|
||||||
sample: ["interface eth1/5", "switchport access vlan 20"]
|
sample: ["interface eth1/5", "switchport access vlan 20"]
|
||||||
changed:
|
|
||||||
description: check to see if a change was made on the device
|
|
||||||
returned: always
|
|
||||||
type: boolean
|
|
||||||
sample: true
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ansible.module_utils.nxos import get_config, load_config, run_commands
|
from ansible.module_utils.nxos import load_config, run_commands
|
||||||
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
from ansible.module_utils.nxos import nxos_argument_spec, check_args
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
|
|
||||||
import re
|
|
||||||
import re
|
|
||||||
|
|
||||||
def get_interface_type(interface):
|
def get_interface_type(interface):
|
||||||
"""Gets the type of interface
|
"""Gets the type of interface
|
||||||
Args:
|
Args:
|
||||||
|
@ -201,12 +168,12 @@ def get_interface_mode(interface, module):
|
||||||
"""
|
"""
|
||||||
command = 'show interface ' + interface
|
command = 'show interface ' + interface
|
||||||
intf_type = get_interface_type(interface)
|
intf_type = get_interface_type(interface)
|
||||||
body = execute_show_command(command, module)
|
|
||||||
mode = 'unknown'
|
mode = 'unknown'
|
||||||
interface_table = {}
|
interface_table = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
interface_table = body[0]['TABLE_interface']['ROW_interface']
|
body = execute_show_command(command, module)[0]
|
||||||
|
interface_table = body['TABLE_interface']['ROW_interface']
|
||||||
except (KeyError, AttributeError, IndexError):
|
except (KeyError, AttributeError, IndexError):
|
||||||
return mode
|
return mode
|
||||||
|
|
||||||
|
@ -231,11 +198,12 @@ def interface_is_portchannel(interface, module):
|
||||||
True/False based on if interface is a member of a portchannel bundle
|
True/False based on if interface is a member of a portchannel bundle
|
||||||
"""
|
"""
|
||||||
intf_type = get_interface_type(interface)
|
intf_type = get_interface_type(interface)
|
||||||
|
|
||||||
if intf_type == 'ethernet':
|
if intf_type == 'ethernet':
|
||||||
command = 'show interface ' + interface
|
command = 'show interface ' + interface
|
||||||
body = execute_show_command(command, module)
|
|
||||||
try:
|
try:
|
||||||
interface_table = body[0]['TABLE_interface']['ROW_interface']
|
body = execute_show_command(command, module)[0]
|
||||||
|
interface_table = body['TABLE_interface']['ROW_interface']
|
||||||
except (KeyError, AttributeError, IndexError):
|
except (KeyError, AttributeError, IndexError):
|
||||||
interface_table = None
|
interface_table = None
|
||||||
|
|
||||||
|
@ -261,14 +229,13 @@ def get_switchport(port, module):
|
||||||
|
|
||||||
command = 'show interface {0} switchport'.format(port)
|
command = 'show interface {0} switchport'.format(port)
|
||||||
|
|
||||||
body = execute_show_command(command, module)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
body = execute_show_command(command, module)[0]
|
body = execute_show_command(command, module)[0]
|
||||||
except IndexError:
|
sp_table = body['TABLE_interface']['ROW_interface']
|
||||||
body = []
|
except (KeyError, AttributeError, IndexError):
|
||||||
|
sp_table = None
|
||||||
|
|
||||||
if body:
|
if sp_table:
|
||||||
key_map = {
|
key_map = {
|
||||||
"interface": "interface",
|
"interface": "interface",
|
||||||
"oper_mode": "mode",
|
"oper_mode": "mode",
|
||||||
|
@ -279,12 +246,9 @@ def get_switchport(port, module):
|
||||||
"native_vlan_name": "native_vlan_name",
|
"native_vlan_name": "native_vlan_name",
|
||||||
"trunk_vlans": "trunk_vlans"
|
"trunk_vlans": "trunk_vlans"
|
||||||
}
|
}
|
||||||
|
|
||||||
sp_table = body['TABLE_interface']['ROW_interface']
|
|
||||||
|
|
||||||
sp = apply_key_map(key_map, sp_table)
|
sp = apply_key_map(key_map, sp_table)
|
||||||
|
|
||||||
return sp
|
return sp
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@ -293,28 +257,32 @@ def remove_switchport_config_commands(interface, existing, proposed, module):
|
||||||
mode = proposed.get('mode')
|
mode = proposed.get('mode')
|
||||||
commands = []
|
commands = []
|
||||||
command = None
|
command = None
|
||||||
|
|
||||||
if mode == 'access':
|
if mode == 'access':
|
||||||
av_check = existing.get('access_vlan') == proposed.get('access_vlan')
|
av_check = existing.get('access_vlan') == proposed.get('access_vlan')
|
||||||
if av_check:
|
if av_check:
|
||||||
command = 'no switchport access vlan {0}'.format(
|
command = 'no switchport access vlan {0}'.format(existing.get('access_vlan'))
|
||||||
existing.get('access_vlan'))
|
|
||||||
commands.append(command)
|
commands.append(command)
|
||||||
|
|
||||||
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:
|
||||||
existing_vlans = existing.get('trunk_vlans_list')
|
existing_vlans = existing.get('trunk_vlans_list')
|
||||||
proposed_vlans = proposed.get('trunk_vlans_list')
|
proposed_vlans = proposed.get('trunk_vlans_list')
|
||||||
vlans_to_remove = set(proposed_vlans).intersection(existing_vlans)
|
vlans_to_remove = set(proposed_vlans).intersection(existing_vlans)
|
||||||
|
|
||||||
if vlans_to_remove:
|
if vlans_to_remove:
|
||||||
command = 'switchport trunk allowed vlan remove {0}'.format(
|
proposed_allowed_vlans = proposed.get('trunk_allowed_vlans')
|
||||||
proposed.get('trunk_vlans', proposed.get('trunk_allowed_vlans')))
|
remove_trunk_allowed_vlans = proposed.get('trunk_vlans', proposed_allowed_vlans)
|
||||||
|
command = 'switchport trunk allowed vlan remove {0}'.format(remove_trunk_allowed_vlans)
|
||||||
commands.append(command)
|
commands.append(command)
|
||||||
native_check = existing.get(
|
|
||||||
'native_vlan') == proposed.get('native_vlan')
|
native_check = existing.get('native_vlan') == proposed.get('native_vlan')
|
||||||
if native_check and proposed.get('native_vlan'):
|
if native_check and proposed.get('native_vlan'):
|
||||||
command = 'no switchport trunk native vlan {0}'.format(
|
command = 'no switchport trunk native vlan {0}'.format(existing.get('native_vlan'))
|
||||||
existing.get('native_vlan'))
|
|
||||||
commands.append(command)
|
commands.append(command)
|
||||||
|
|
||||||
if commands:
|
if commands:
|
||||||
commands.insert(0, 'interface ' + interface)
|
commands.insert(0, 'interface ' + interface)
|
||||||
return commands
|
return commands
|
||||||
|
@ -326,29 +294,32 @@ def get_switchport_config_commands(interface, existing, proposed, module):
|
||||||
|
|
||||||
proposed_mode = proposed.get('mode')
|
proposed_mode = proposed.get('mode')
|
||||||
existing_mode = existing.get('mode')
|
existing_mode = existing.get('mode')
|
||||||
|
|
||||||
commands = []
|
commands = []
|
||||||
command = None
|
command = None
|
||||||
|
|
||||||
if proposed_mode != existing_mode:
|
if proposed_mode != existing_mode:
|
||||||
if proposed_mode == 'trunk':
|
if proposed_mode == 'trunk':
|
||||||
command = 'switchport mode trunk'
|
command = 'switchport mode trunk'
|
||||||
elif proposed_mode == 'access':
|
elif proposed_mode == 'access':
|
||||||
command = 'switchport mode access'
|
command = 'switchport mode access'
|
||||||
|
|
||||||
if command:
|
if command:
|
||||||
commands.append(command)
|
commands.append(command)
|
||||||
|
|
||||||
if proposed_mode == 'access':
|
if proposed_mode == 'access':
|
||||||
av_check = existing.get('access_vlan') == proposed.get('access_vlan')
|
av_check = existing.get('access_vlan') == proposed.get('access_vlan')
|
||||||
if not av_check:
|
if not av_check:
|
||||||
command = 'switchport access vlan {0}'.format(
|
command = 'switchport access vlan {0}'.format(proposed.get('access_vlan'))
|
||||||
proposed.get('access_vlan'))
|
|
||||||
commands.append(command)
|
commands.append(command)
|
||||||
|
|
||||||
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:
|
||||||
if proposed.get('allowed'):
|
if proposed.get('allowed'):
|
||||||
command = 'switchport trunk allowed vlan {0}'.format(proposed.get('trunk_allowed_vlans'))
|
command = 'switchport trunk allowed vlan {0}'.format(proposed.get('trunk_allowed_vlans'))
|
||||||
commands.append(command)
|
commands.append(command)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
existing_vlans = existing.get('trunk_vlans_list')
|
existing_vlans = existing.get('trunk_vlans_list')
|
||||||
proposed_vlans = proposed.get('trunk_vlans_list')
|
proposed_vlans = proposed.get('trunk_vlans_list')
|
||||||
|
@ -357,12 +328,11 @@ def get_switchport_config_commands(interface, existing, proposed, module):
|
||||||
command = 'switchport trunk allowed vlan add {0}'.format(proposed.get('trunk_vlans'))
|
command = 'switchport trunk allowed vlan add {0}'.format(proposed.get('trunk_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')
|
|
||||||
if not native_check and proposed.get('native_vlan'):
|
if not native_check and proposed.get('native_vlan'):
|
||||||
command = 'switchport trunk native vlan {0}'.format(
|
command = 'switchport trunk native vlan {0}'.format(proposed.get('native_vlan'))
|
||||||
proposed.get('native_vlan'))
|
|
||||||
commands.append(command)
|
commands.append(command)
|
||||||
|
|
||||||
if commands:
|
if commands:
|
||||||
commands.insert(0, 'interface ' + interface)
|
commands.insert(0, 'interface ' + interface)
|
||||||
return commands
|
return commands
|
||||||
|
@ -417,9 +387,13 @@ def vlan_range_to_list(vlans):
|
||||||
def get_list_of_vlans(module):
|
def get_list_of_vlans(module):
|
||||||
|
|
||||||
command = 'show vlan'
|
command = 'show vlan'
|
||||||
body = execute_show_command(command, module)
|
|
||||||
vlan_list = []
|
vlan_list = []
|
||||||
vlan_table = body[0].get('TABLE_vlanbrief')['ROW_vlanbrief']
|
|
||||||
|
try:
|
||||||
|
body = execute_show_command(command, module)[0]
|
||||||
|
vlan_table = body['TABLE_vlanbrief']['ROW_vlanbrief']
|
||||||
|
except (KeyError, AttributeError, IndexError):
|
||||||
|
return []
|
||||||
|
|
||||||
if isinstance(vlan_table, list):
|
if isinstance(vlan_table, list):
|
||||||
for vlan in vlan_table:
|
for vlan in vlan_table:
|
||||||
|
@ -449,7 +423,7 @@ def apply_key_map(key_map, table):
|
||||||
for key, value in table.items():
|
for key, value in table.items():
|
||||||
new_key = key_map.get(key)
|
new_key = key_map.get(key)
|
||||||
if new_key:
|
if new_key:
|
||||||
new_dict[new_key] = str(value)
|
new_dict[new_key] = value
|
||||||
return new_dict
|
return new_dict
|
||||||
|
|
||||||
|
|
||||||
|
@ -461,8 +435,7 @@ def apply_value_map(value_map, resource):
|
||||||
|
|
||||||
def execute_show_command(command, module, command_type='cli_show'):
|
def execute_show_command(command, module, command_type='cli_show'):
|
||||||
if module.params['transport'] == 'cli':
|
if module.params['transport'] == 'cli':
|
||||||
if 'status' not in command:
|
command += ' | json'
|
||||||
command += ' | json'
|
|
||||||
cmds = [command]
|
cmds = [command]
|
||||||
body = run_commands(module, cmds)
|
body = run_commands(module, cmds)
|
||||||
elif module.params['transport'] == 'nxapi':
|
elif module.params['transport'] == 'nxapi':
|
||||||
|
@ -491,22 +464,22 @@ def main():
|
||||||
native_vlan=dict(type='str', required=False),
|
native_vlan=dict(type='str', required=False),
|
||||||
trunk_vlans=dict(type='str', aliases=['trunk_add_vlans'], required=False),
|
trunk_vlans=dict(type='str', aliases=['trunk_add_vlans'], required=False),
|
||||||
trunk_allowed_vlans=dict(type='str', 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')
|
|
||||||
)
|
)
|
||||||
|
|
||||||
argument_spec.update(nxos_argument_spec)
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
module = AnsibleModule(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']],
|
['access_vlan', 'trunk_allowed_vlans']],
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
warnings = list()
|
warnings = list()
|
||||||
|
commands = []
|
||||||
|
results = {'changed': False}
|
||||||
check_args(module, warnings)
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
||||||
interface = module.params['interface']
|
interface = module.params['interface']
|
||||||
mode = module.params['mode']
|
mode = module.params['mode']
|
||||||
access_vlan = module.params['access_vlan']
|
access_vlan = module.params['access_vlan']
|
||||||
|
@ -524,12 +497,10 @@ def main():
|
||||||
interface = interface.lower()
|
interface = interface.lower()
|
||||||
|
|
||||||
if mode == 'access' and state == 'present' and not access_vlan:
|
if mode == 'access' and state == 'present' and not access_vlan:
|
||||||
module.fail_json(msg='access_vlan param is required when '
|
module.fail_json(msg='access_vlan param is required when mode=access && state=present')
|
||||||
'mode=access && state=present')
|
|
||||||
|
|
||||||
if mode == 'trunk' and access_vlan:
|
if mode == 'trunk' and access_vlan:
|
||||||
module.fail_json(msg='access_vlan param not supported when '
|
module.fail_json(msg='access_vlan param not supported when using mode=trunk')
|
||||||
'using mode=trunk')
|
|
||||||
|
|
||||||
current_mode = get_interface_mode(interface, module)
|
current_mode = get_interface_mode(interface, module)
|
||||||
|
|
||||||
|
@ -552,6 +523,18 @@ def main():
|
||||||
if not existing:
|
if not existing:
|
||||||
module.fail_json(msg='Make sure you are using the FULL interface name')
|
module.fail_json(msg='Make sure you are using the FULL interface name')
|
||||||
|
|
||||||
|
if trunk_vlans or trunk_allowed_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['trunk_vlans']))
|
||||||
|
|
||||||
|
existing['trunk_vlans_list'] = existing_trunks_list
|
||||||
|
proposed['trunk_vlans_list'] = trunk_vlans_list
|
||||||
|
|
||||||
current_vlans = get_list_of_vlans(module)
|
current_vlans = get_list_of_vlans(module)
|
||||||
|
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
|
@ -563,65 +546,37 @@ def main():
|
||||||
module.fail_json(msg='You are trying to configure a VLAN'
|
module.fail_json(msg='You are trying to configure a VLAN'
|
||||||
' 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)
|
||||||
|
else:
|
||||||
if trunk_vlans or trunk_allowed_vlans:
|
command = get_switchport_config_commands(interface, existing, proposed, module)
|
||||||
if trunk_vlans:
|
commands.append(command)
|
||||||
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['trunk_vlans'])
|
|
||||||
)
|
|
||||||
|
|
||||||
existing['trunk_vlans_list'] = existing_trunks_list
|
|
||||||
proposed['trunk_vlans_list'] = trunk_vlans_list
|
|
||||||
|
|
||||||
changed = False
|
|
||||||
|
|
||||||
commands = []
|
|
||||||
if state == 'present':
|
|
||||||
command = get_switchport_config_commands(interface, existing, proposed, module)
|
|
||||||
commands.append(command)
|
|
||||||
elif state == 'unconfigured':
|
elif state == 'unconfigured':
|
||||||
is_default = is_switchport_default(existing)
|
is_default = is_switchport_default(existing)
|
||||||
if not is_default:
|
if not is_default:
|
||||||
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, existing,
|
command = remove_switchport_config_commands(interface, existing, proposed, module)
|
||||||
proposed, module)
|
|
||||||
commands.append(command)
|
commands.append(command)
|
||||||
|
|
||||||
if trunk_vlans or trunk_allowed_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')
|
||||||
|
|
||||||
end_state = existing
|
|
||||||
|
|
||||||
cmds = flatten_list(commands)
|
cmds = flatten_list(commands)
|
||||||
|
|
||||||
if cmds:
|
if cmds:
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, commands=cmds)
|
module.exit_json(changed=True, commands=cmds)
|
||||||
else:
|
else:
|
||||||
changed = True
|
results['changed'] = True
|
||||||
load_config(module, cmds)
|
load_config(module, cmds)
|
||||||
end_state = get_switchport(interface, module)
|
|
||||||
if 'configure' in cmds:
|
if 'configure' in cmds:
|
||||||
cmds.pop(0)
|
cmds.pop(0)
|
||||||
|
|
||||||
results = {}
|
results['commands'] = cmds
|
||||||
results['proposed'] = proposed
|
|
||||||
results['existing'] = existing
|
|
||||||
results['end_state'] = end_state
|
|
||||||
results['updates'] = cmds
|
|
||||||
results['changed'] = changed
|
|
||||||
results['warnings'] = warnings
|
results['warnings'] = warnings
|
||||||
|
|
||||||
module.exit_json(**results)
|
module.exit_json(**results)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
|
@ -378,7 +378,6 @@ lib/ansible/modules/network/nxos/nxos_snmp_location.py
|
||||||
lib/ansible/modules/network/nxos/nxos_snmp_traps.py
|
lib/ansible/modules/network/nxos/nxos_snmp_traps.py
|
||||||
lib/ansible/modules/network/nxos/nxos_snmp_user.py
|
lib/ansible/modules/network/nxos/nxos_snmp_user.py
|
||||||
lib/ansible/modules/network/nxos/nxos_static_route.py
|
lib/ansible/modules/network/nxos/nxos_static_route.py
|
||||||
lib/ansible/modules/network/nxos/nxos_switchport.py
|
|
||||||
lib/ansible/modules/network/nxos/nxos_system.py
|
lib/ansible/modules/network/nxos/nxos_system.py
|
||||||
lib/ansible/modules/network/nxos/nxos_udld.py
|
lib/ansible/modules/network/nxos/nxos_udld.py
|
||||||
lib/ansible/modules/network/nxos/nxos_udld_interface.py
|
lib/ansible/modules/network/nxos/nxos_udld_interface.py
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
{
|
||||||
|
"TABLE_interface": {
|
||||||
|
"ROW_interface": {
|
||||||
|
"interface": "Ethernet2/1",
|
||||||
|
"state": "up",
|
||||||
|
"admin_state": "up",
|
||||||
|
"share_state": "Dedicated",
|
||||||
|
"eth_hw_desc": "Ethernet",
|
||||||
|
"eth_hw_addr": "fa16.3e00.0001",
|
||||||
|
"eth_bia_addr": "fa16.3e27.f279",
|
||||||
|
"desc": "to nxos03",
|
||||||
|
"eth_ip_addr": "10.0.0.45",
|
||||||
|
"eth_ip_mask": 30,
|
||||||
|
"eth_ip_prefix": "10.0.0.44",
|
||||||
|
"eth_mtu": "1500",
|
||||||
|
"eth_bw": 1000000,
|
||||||
|
"eth_dly": 10,
|
||||||
|
"eth_reliability": "255",
|
||||||
|
"eth_txload": "1",
|
||||||
|
"eth_rxload": "1",
|
||||||
|
"medium": "broadcast",
|
||||||
|
"eth_mode": "access",
|
||||||
|
"eth_bundle": 0,
|
||||||
|
"eth_duplex": "full",
|
||||||
|
"eth_speed": "1000 Mb/s",
|
||||||
|
"eth_beacon": "off",
|
||||||
|
"eth_autoneg": "off",
|
||||||
|
"eth_in_flowctrl": "off",
|
||||||
|
"eth_out_flowctrl": "off",
|
||||||
|
"eth_mdix": "off",
|
||||||
|
"eth_swt_monitor": "off",
|
||||||
|
"eth_ethertype": "0x8100",
|
||||||
|
"eth_eee_state": "n/a",
|
||||||
|
"eth_link_flapped": "13week(s) 0day(s)",
|
||||||
|
"eth_clear_counters": "never",
|
||||||
|
"eth_reset_cntr": 1,
|
||||||
|
"eth_load_interval1_rx": 0,
|
||||||
|
"eth_inrate1_bits": 0,
|
||||||
|
"eth_inrate1_pkts": 0,
|
||||||
|
"eth_load_interval1_tx": 0,
|
||||||
|
"eth_outrate1_bits": 0,
|
||||||
|
"eth_outrate1_pkts": 0,
|
||||||
|
"eth_inrate1_summary_bits": "0 bps",
|
||||||
|
"eth_inrate1_summary_pkts": "0 pps",
|
||||||
|
"eth_outrate1_summary_bits": "0 bps",
|
||||||
|
"eth_outrate1_summary_pkts": "0 pps",
|
||||||
|
"eth_load_interval2_rx": 0,
|
||||||
|
"eth_inrate2_bits": 0,
|
||||||
|
"eth_inrate2_pkts": 0,
|
||||||
|
"eth_load_interval2_tx": 0,
|
||||||
|
"eth_outrate2_bits": 0,
|
||||||
|
"eth_outrate2_pkts": 0,
|
||||||
|
"eth_inrate2_summary_bits": "0 bps",
|
||||||
|
"eth_inrate2_summary_pkts": "0 pps",
|
||||||
|
"eth_outrate2_summary_bits": "0 bps",
|
||||||
|
"eth_outrate2_summary_pkts": "0 pps",
|
||||||
|
"eth_inucast": 0,
|
||||||
|
"eth_inmcast": 0,
|
||||||
|
"eth_inbcast": 0,
|
||||||
|
"eth_inpkts": 0,
|
||||||
|
"eth_inbytes": 0,
|
||||||
|
"eth_jumbo_inpkts": 0,
|
||||||
|
"eth_storm_supp": 0,
|
||||||
|
"eth_runts": 0,
|
||||||
|
"eth_giants": 0,
|
||||||
|
"eth_crc": 0,
|
||||||
|
"eth_nobuf": 0,
|
||||||
|
"eth_inerr": 0,
|
||||||
|
"eth_frame": 0,
|
||||||
|
"eth_overrun": 0,
|
||||||
|
"eth_underrun": 0,
|
||||||
|
"eth_ignored": 0,
|
||||||
|
"eth_watchdog": 0,
|
||||||
|
"eth_bad_eth": 0,
|
||||||
|
"eth_bad_proto": 0,
|
||||||
|
"eth_in_ifdown_drops": 0,
|
||||||
|
"eth_dribble": 0,
|
||||||
|
"eth_indiscard": 0,
|
||||||
|
"eth_inpause": 0,
|
||||||
|
"eth_outucast": 0,
|
||||||
|
"eth_outmcast": 0,
|
||||||
|
"eth_outbcast": 0,
|
||||||
|
"eth_outpkts": 0,
|
||||||
|
"eth_outbytes": 0,
|
||||||
|
"eth_jumbo_outpkts": 0,
|
||||||
|
"eth_outerr": 0,
|
||||||
|
"eth_coll": 0,
|
||||||
|
"eth_deferred": 0,
|
||||||
|
"eth_latecoll": 0,
|
||||||
|
"eth_lostcarrier": 0,
|
||||||
|
"eth_nocarrier": 0,
|
||||||
|
"eth_babbles": 0,
|
||||||
|
"eth_outdiscard": 0,
|
||||||
|
"eth_outpause": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"TABLE_interface": {
|
||||||
|
"ROW_interface": {
|
||||||
|
"interface": "Ethernet2/1",
|
||||||
|
"oper_mode": "access",
|
||||||
|
"switchport": "Enabled",
|
||||||
|
"access_vlan": 2,
|
||||||
|
"access_vlan_name": "VLAN2",
|
||||||
|
"native_vlan": 10,
|
||||||
|
"native_vlan_name": "VLAN10",
|
||||||
|
"trunk_vlans": "1-50"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"TABLE_vlanbrief": {
|
||||||
|
"ROW_vlanbrief": {
|
||||||
|
"vlanshowbr-vlanid": 1,
|
||||||
|
"vlanshowbr-vlanid-utf": 1,
|
||||||
|
"vlanshowbr-vlanname": "VLAN1",
|
||||||
|
"vlanshowbr-vlanstate": "active",
|
||||||
|
"vlanshowbr-shutstate": "noshutdown"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"TABLE_mtuinfo": {
|
||||||
|
"ROW_mtuinfo": {
|
||||||
|
"vlanshowinfo-vlanid": 1,
|
||||||
|
"vlanshowinfo-media-type": "enet",
|
||||||
|
"vlanshowinfo-vlanmode": "ce-vlan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
# (c) 2016 Red Hat Inc.
|
||||||
|
#
|
||||||
|
# This file is part of Ansible
|
||||||
|
#
|
||||||
|
# Ansible is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Ansible is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Make coding more python3-ish
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from ansible.compat.tests.mock import patch
|
||||||
|
from ansible.modules.network.nxos import nxos_switchport
|
||||||
|
from .nxos_module import TestNxosModule, load_fixture, set_module_args
|
||||||
|
|
||||||
|
|
||||||
|
class TestNxosSwitchportModule(TestNxosModule):
|
||||||
|
|
||||||
|
module = nxos_switchport
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.mock_run_commands = patch('ansible.modules.network.nxos.nxos_switchport.run_commands')
|
||||||
|
self.run_commands = self.mock_run_commands.start()
|
||||||
|
|
||||||
|
self.mock_load_config = patch('ansible.modules.network.nxos.nxos_switchport.load_config')
|
||||||
|
self.load_config = self.mock_load_config.start()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.mock_run_commands.stop()
|
||||||
|
self.mock_load_config.stop()
|
||||||
|
|
||||||
|
def load_fixtures(self, commands=None, device=''):
|
||||||
|
def load_from_file(*args, **kwargs):
|
||||||
|
module, commands = args
|
||||||
|
output = list()
|
||||||
|
for command in commands:
|
||||||
|
filename = str(command).split(' | ')[0].replace(' ', '_')
|
||||||
|
filename = filename.replace('2/1', '')
|
||||||
|
output.append(load_fixture('nxos_switchport', filename))
|
||||||
|
return output
|
||||||
|
|
||||||
|
self.run_commands.side_effect = load_from_file
|
||||||
|
self.load_config.return_value = None
|
||||||
|
|
||||||
|
def test_nxos_switchport_present(self):
|
||||||
|
set_module_args(dict(interface='Ethernet2/1', mode='access', access_vlan=1, state='present'))
|
||||||
|
result = self.execute_module(changed=True)
|
||||||
|
self.assertEqual(result['commands'], ['interface ethernet2/1', 'switchport access vlan 1'])
|
||||||
|
|
||||||
|
def test_nxos_switchport_unconfigured(self):
|
||||||
|
set_module_args(dict(interface='Ethernet2/1', state='unconfigured'))
|
||||||
|
result = self.execute_module(changed=True)
|
||||||
|
self.assertEqual(result['commands'], ['interface ethernet2/1',
|
||||||
|
'switchport mode access',
|
||||||
|
'switch access vlan 1',
|
||||||
|
'switchport trunk native vlan 1',
|
||||||
|
'switchport trunk allowed vlan all'])
|
||||||
|
|
||||||
|
def test_nxos_switchport_absent(self):
|
||||||
|
set_module_args(dict(interface='Ethernet2/1', mode='access', access_vlan=3, state='absent'))
|
||||||
|
result = self.execute_module(changed=False)
|
||||||
|
self.assertEqual(result['commands'], [])
|
Loading…
Reference in New Issue