Add Switchport mode support in newly introduced *_l2_interfaces resource module (#67456)
* fixes 65032
This commit is contained in:
parent
a18d3257e9
commit
d8c5c6eb9d
24 changed files with 120 additions and 20 deletions
|
@ -41,6 +41,7 @@ class L2_interfacesArgs(object):
|
|||
'options': {
|
||||
'access': {'options': {'vlan': {'type': 'int'}},
|
||||
'type': 'dict'},
|
||||
'mode': {'type': 'str', 'choices': ['access', 'trunk']},
|
||||
'name': {'required': True, 'type': 'str'},
|
||||
'trunk': {'options': {'native_vlan': {'type': 'int'}, 'trunk_allowed_vlans': {'type': 'list'}},
|
||||
'type': 'dict'}},
|
||||
|
|
|
@ -207,6 +207,11 @@ class L2_interfaces(ConfigBase):
|
|||
|
||||
def set_interface(want, have):
|
||||
commands = []
|
||||
|
||||
want_mode = want.get("mode")
|
||||
if want_mode and want_mode != have.get("mode"):
|
||||
commands.append("switchport mode {0}".format(want_mode))
|
||||
|
||||
wants_access = want.get("access")
|
||||
if wants_access:
|
||||
access_vlan = wants_access.get("vlan")
|
||||
|
@ -229,6 +234,10 @@ def set_interface(want, have):
|
|||
|
||||
def clear_interface(want, have):
|
||||
commands = []
|
||||
|
||||
if 'mode' in have and want.get('mode') is None:
|
||||
commands.append("no switchport mode")
|
||||
|
||||
if "access" in have and not want.get('access'):
|
||||
commands.append("no switchport access vlan")
|
||||
|
||||
|
|
|
@ -81,6 +81,9 @@ class L2_interfacesFacts(object):
|
|||
|
||||
# populate the facts from the configuration
|
||||
config['name'] = re.match(r'(\S+)', conf).group(1).replace('"', '')
|
||||
has_mode = re.search(r"switchport mode (\S+)", conf)
|
||||
if has_mode:
|
||||
config["mode"] = has_mode.group(1)
|
||||
|
||||
has_access = re.search(r"switchport access vlan (\d+)", conf)
|
||||
if has_access:
|
||||
|
|
|
@ -36,6 +36,7 @@ class L2_InterfacesArgs(object):
|
|||
|
||||
argument_spec = {'config': {'elements': 'dict',
|
||||
'options': {'name': {'type': 'str', 'required': True},
|
||||
'mode': {'type': 'str', 'choices': ['access', 'trunk']},
|
||||
'access': {'type': 'dict',
|
||||
'options': {'vlan': {'type': 'int'}}
|
||||
},
|
||||
|
|
|
@ -246,9 +246,12 @@ class L2_Interfaces(ConfigBase):
|
|||
|
||||
if diff:
|
||||
diff = dict(diff)
|
||||
mode = diff.get('mode')
|
||||
access = diff.get('access')
|
||||
trunk = diff.get('trunk')
|
||||
|
||||
if diff.get('access'):
|
||||
cmd = 'switchport access vlan {0}'.format(diff.get('access')[0][1])
|
||||
if access:
|
||||
cmd = 'switchport access vlan {0}'.format(access[0][1])
|
||||
add_command_to_config_list(interface, cmd, commands)
|
||||
|
||||
if diff.get('voice'):
|
||||
|
@ -256,8 +259,8 @@ class L2_Interfaces(ConfigBase):
|
|||
add_command_to_config_list(interface, cmd, commands)
|
||||
|
||||
if want_trunk:
|
||||
if diff.get('trunk'):
|
||||
diff = dict(diff.get('trunk'))
|
||||
if trunk:
|
||||
diff = dict(trunk)
|
||||
if diff.get('encapsulation'):
|
||||
cmd = self.trunk_cmds['encapsulation'] + ' {0}'.format(diff.get('encapsulation'))
|
||||
add_command_to_config_list(interface, cmd, commands)
|
||||
|
@ -276,6 +279,10 @@ class L2_Interfaces(ConfigBase):
|
|||
cmd = self.trunk_cmds['pruning_vlans'] + ' {0}'.format(pruning_vlans)
|
||||
add_command_to_config_list(interface, cmd, commands)
|
||||
|
||||
if mode:
|
||||
cmd = 'switchport mode {0}'.format(mode)
|
||||
add_command_to_config_list(interface, cmd, commands)
|
||||
|
||||
return commands
|
||||
|
||||
def _clear_config(self, want, have):
|
||||
|
@ -286,6 +293,9 @@ class L2_Interfaces(ConfigBase):
|
|||
else:
|
||||
interface = 'interface ' + have['name']
|
||||
|
||||
if have.get('mode') or want.get('mode'):
|
||||
remove_command_from_config_list(interface, 'switchport mode', commands)
|
||||
|
||||
if have.get('access') and want.get('access') is None:
|
||||
remove_command_from_config_list(interface, L2_Interfaces.access_cmds['access_vlan'], commands)
|
||||
elif have.get('access') and want.get('access'):
|
||||
|
|
|
@ -86,7 +86,9 @@ class L2_InterfacesFacts(object):
|
|||
if intf.upper()[:2] in ('HU', 'FO', 'TW', 'TE', 'GI', 'FA', 'ET', 'PO'):
|
||||
# populate the facts from the configuration
|
||||
config['name'] = normalize_interface(intf)
|
||||
|
||||
has_mode = utils.parse_conf_arg(conf, 'switchport mode')
|
||||
if has_mode:
|
||||
config['mode'] = has_mode
|
||||
has_access = utils.parse_conf_arg(conf, 'switchport access vlan')
|
||||
if has_access:
|
||||
config["access"] = {"vlan": int(has_access)}
|
||||
|
|
|
@ -82,7 +82,8 @@ def filter_dict_having_none_value(want, have):
|
|||
# then apply the new change
|
||||
dict_val = have.get(k)[0].get(key)
|
||||
test_key_dict.update({key: dict_val})
|
||||
test_dict.update({k: test_key_dict})
|
||||
if test_key_dict:
|
||||
test_dict.update({k: test_key_dict})
|
||||
if isinstance(v, list):
|
||||
for key, value in iteritems(v[0]):
|
||||
test_key_dict = dict()
|
||||
|
@ -92,7 +93,8 @@ def filter_dict_having_none_value(want, have):
|
|||
elif k == 'ipv6' and value.lower() != have.get(k)[0].get(key).lower():
|
||||
dict_val = have.get(k)[0].get(key)
|
||||
test_key_dict.update({key: dict_val})
|
||||
test_dict.update({k: test_key_dict})
|
||||
if test_key_dict:
|
||||
test_dict.update({k: test_key_dict})
|
||||
# below conditions checks are added to check if
|
||||
# secondary IP is configured, if yes then delete
|
||||
# the already configured IP if want and have IP
|
||||
|
|
|
@ -47,6 +47,10 @@ class L2_interfacesArgs(object): # pylint: disable=R0903
|
|||
},
|
||||
'type': 'dict'
|
||||
},
|
||||
'mode': {
|
||||
'type': 'str',
|
||||
'choices': ['access', 'trunk']
|
||||
},
|
||||
'name': {
|
||||
'required': True,
|
||||
'type': 'str'
|
||||
|
|
|
@ -154,7 +154,7 @@ class L2_interfaces(ConfigBase):
|
|||
merged_commands = self.set_commands(w, have, True)
|
||||
if 'name' not in diff:
|
||||
diff['name'] = w['name']
|
||||
wkeys = w.keys()
|
||||
|
||||
dkeys = diff.keys()
|
||||
for k in w.copy():
|
||||
if k in self.exclude_params and k in dkeys:
|
||||
|
@ -231,6 +231,8 @@ class L2_interfaces(ConfigBase):
|
|||
cmd = 'no switchport '
|
||||
if 'vlan' in obj:
|
||||
commands.append(cmd + 'access vlan')
|
||||
if 'mode' in obj:
|
||||
commands.append(cmd + 'mode')
|
||||
if 'allowed_vlans' in obj:
|
||||
commands.append(cmd + 'trunk allowed vlan')
|
||||
if 'native_vlan' in obj:
|
||||
|
@ -252,6 +254,8 @@ class L2_interfaces(ConfigBase):
|
|||
return commands
|
||||
|
||||
cmd = 'switchport '
|
||||
if 'mode' in d:
|
||||
commands.append(cmd + 'mode {0}'.format(d['mode']))
|
||||
if 'vlan' in d:
|
||||
commands.append(cmd + 'access vlan ' + str(d['vlan']))
|
||||
if 'allowed_vlans' in d:
|
||||
|
@ -274,6 +278,8 @@ class L2_interfaces(ConfigBase):
|
|||
else:
|
||||
diff = self.diff_of_dicts(w, obj_in_have)
|
||||
if diff and not replace:
|
||||
if 'mode' in diff.keys() and diff['mode']:
|
||||
commands = self.add_commands(diff)
|
||||
if "allowed_vlans" in diff.keys() and diff["allowed_vlans"]:
|
||||
vlan_tobe_added = diff["allowed_vlans"].split(',')
|
||||
vlan_list = vlan_tobe_added[:]
|
||||
|
|
|
@ -85,6 +85,7 @@ class L2_interfacesFacts(object):
|
|||
return {}
|
||||
|
||||
config['name'] = intf
|
||||
config['mode'] = utils.parse_conf_arg(conf, 'switchport mode')
|
||||
config['ip_forward'] = utils.parse_conf_arg(conf, 'ip forward')
|
||||
config['access']['vlan'] = utils.parse_conf_arg(conf, 'switchport access vlan')
|
||||
config['trunk']['allowed_vlans'] = utils.parse_conf_arg(conf, 'switchport trunk allowed vlan')
|
||||
|
|
|
@ -79,6 +79,15 @@ options:
|
|||
- List of allowed VLANs in a given trunk port. These are the only VLANs that will be
|
||||
configured on the trunk.
|
||||
type: list
|
||||
mode:
|
||||
description:
|
||||
- Mode in which interface needs to be configured.
|
||||
- Access mode is not shown in interface facts, so idempotency will not be
|
||||
maintained for switchport mode access and every time the output will come
|
||||
as changed=True.
|
||||
version_added: '2.10'
|
||||
type: str
|
||||
choices: ['access', 'trunk']
|
||||
state:
|
||||
choices:
|
||||
- merged
|
||||
|
|
|
@ -99,6 +99,13 @@ options:
|
|||
description:
|
||||
- Pruning VLAN to be configured in trunk port. It's used as the trunk pruning VLAN ID.
|
||||
type: list
|
||||
mode:
|
||||
description:
|
||||
- Mode in which interface needs to be configured.
|
||||
- An interface whose trunk encapsulation is "Auto" can not be configured to "trunk" mode.
|
||||
version_added: '2.10'
|
||||
type: str
|
||||
choices: ['access', 'trunk']
|
||||
state:
|
||||
choices:
|
||||
- merged
|
||||
|
@ -133,11 +140,13 @@ EXAMPLES = """
|
|||
ios_l2_interfaces:
|
||||
config:
|
||||
- name: GigabitEthernet0/1
|
||||
mode: access
|
||||
access:
|
||||
vlan: 10
|
||||
voice:
|
||||
vlan: 40
|
||||
- name: GigabitEthernet0/2
|
||||
mode: trunk
|
||||
trunk:
|
||||
allowed_vlans: 10-20,40
|
||||
native_vlan: 20
|
||||
|
@ -153,6 +162,7 @@ EXAMPLES = """
|
|||
# description Configured by Ansible
|
||||
# switchport access vlan 10
|
||||
# switchport access vlan 40
|
||||
# switchport mode access
|
||||
# negotiation auto
|
||||
# interface GigabitEthernet0/2
|
||||
# description This is test
|
||||
|
@ -160,6 +170,7 @@ EXAMPLES = """
|
|||
# switchport trunk encapsulation dot1q
|
||||
# switchport trunk native vlan 20
|
||||
# switchport trunk pruning vlan 10,20
|
||||
# switchport mode trunk
|
||||
# media-type rj45
|
||||
# negotiation auto
|
||||
|
||||
|
|
|
@ -80,6 +80,15 @@ options:
|
|||
- List of allowed VLANs in a given trunk port. These are the only
|
||||
VLANs that will be configured on the trunk.
|
||||
type: str
|
||||
mode:
|
||||
description:
|
||||
- Mode in which interface needs to be configured.
|
||||
- Access mode is not shown in interface facts, so idempotency will not be
|
||||
maintained for switchport mode access and every time the output will come
|
||||
as changed=True.
|
||||
version_added: '2.10'
|
||||
type: str
|
||||
choices: ['access', 'trunk']
|
||||
|
||||
state:
|
||||
description:
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
interface {{ item }}
|
||||
no switchport mode
|
||||
no switchport access vlan
|
||||
no switchport trunk native vlan
|
||||
with_items:
|
||||
- Ethernet1
|
||||
- Ethernet2
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
- set_fact:
|
||||
config:
|
||||
- name: Ethernet1
|
||||
mode: trunk
|
||||
trunk:
|
||||
native_vlan: 10
|
||||
- name: Ethernet2
|
||||
|
@ -36,11 +37,13 @@
|
|||
- set_fact:
|
||||
expected_config:
|
||||
- name: Ethernet1
|
||||
mode: trunk
|
||||
access:
|
||||
vlan: 20
|
||||
trunk:
|
||||
native_vlan: 10
|
||||
- name: Ethernet2
|
||||
mode: trunk
|
||||
access:
|
||||
vlan: 30
|
||||
trunk:
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
- "ansible_facts.network_resources.l2_interfaces|symmetric_difference(result.after) == []"
|
||||
|
||||
- set_fact:
|
||||
expected_config: "{{ config }} + [{'name': 'Ethernet2', 'trunk': {'native_vlan': 20}}, {'name': 'Management1'}]"
|
||||
expected_config: "{{ config }} + [{'mode': 'trunk', 'name': 'Ethernet2', 'trunk': {'native_vlan': 20}}, {'name': 'Management1'}]"
|
||||
|
||||
- assert:
|
||||
that:
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
config: |
|
||||
interface Ethernet1
|
||||
switchport access vlan 20
|
||||
no switchport mode
|
||||
no switchport trunk native vlan
|
||||
no switchport trunk allowed vlan
|
||||
interface Ethernet2
|
||||
|
@ -21,7 +22,8 @@
|
|||
- name: Ethernet1
|
||||
access:
|
||||
vlan: 20
|
||||
- name: Ethernet2
|
||||
- mode: trunk
|
||||
name: Ethernet2
|
||||
trunk:
|
||||
native_vlan: 20
|
||||
- name: Management1
|
||||
|
|
|
@ -11,4 +11,5 @@
|
|||
switchport trunk native vlan 10
|
||||
switchport trunk allowed vlan 10-20,40
|
||||
switchport trunk pruning vlan 10,20
|
||||
switchport mode trunk
|
||||
when: ansible_net_version != "15.6(2)T"
|
||||
|
|
|
@ -10,10 +10,12 @@
|
|||
no switchport trunk native vlan
|
||||
no switchport trunk allowed vlan
|
||||
no switchport trunk pruning vlan
|
||||
no switchport mode
|
||||
interface GigabitEthernet 0/2
|
||||
no switchport access vlan
|
||||
no switchport trunk encapsulation
|
||||
no switchport trunk native vlan
|
||||
no switchport trunk allowed vlan
|
||||
no switchport trunk pruning vlan
|
||||
no switchport mode
|
||||
when: ansible_net_version != "15.6(2)T"
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
native_vlan: 20
|
||||
pruning_vlans: 10,20
|
||||
encapsulation: dot1q
|
||||
mode: trunk
|
||||
state: merged
|
||||
register: result
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ merged:
|
|||
- "switchport trunk native vlan 20"
|
||||
- "switchport trunk allowed vlan 15-20,40"
|
||||
- "switchport trunk pruning vlan 10,20"
|
||||
- "switchport mode trunk"
|
||||
|
||||
after:
|
||||
- name: GigabitEthernet0/0
|
||||
|
@ -22,7 +23,8 @@ merged:
|
|||
voice:
|
||||
vlan: 40
|
||||
name: GigabitEthernet0/1
|
||||
- name: GigabitEthernet0/2
|
||||
- mode: trunk
|
||||
name: GigabitEthernet0/2
|
||||
trunk:
|
||||
allowed_vlans:
|
||||
- 15-20
|
||||
|
@ -41,7 +43,8 @@ replaced:
|
|||
voice:
|
||||
vlan: 40
|
||||
name: GigabitEthernet0/1
|
||||
- name: GigabitEthernet0/2
|
||||
- mode: trunk
|
||||
name: GigabitEthernet0/2
|
||||
trunk:
|
||||
allowed_vlans:
|
||||
- 10-20
|
||||
|
@ -57,6 +60,7 @@ replaced:
|
|||
- "switchport access vlan 40"
|
||||
- "switchport voice vlan 20"
|
||||
- "interface GigabitEthernet0/2"
|
||||
- "no switchport mode"
|
||||
- "no switchport trunk allowed vlan"
|
||||
- "switchport trunk native vlan 20"
|
||||
- "switchport trunk pruning vlan 10-20,30"
|
||||
|
@ -84,7 +88,8 @@ overridden:
|
|||
voice:
|
||||
vlan: 40
|
||||
name: GigabitEthernet0/1
|
||||
- name: GigabitEthernet0/2
|
||||
- mode: trunk
|
||||
name: GigabitEthernet0/2
|
||||
trunk:
|
||||
allowed_vlans:
|
||||
- 10-20
|
||||
|
@ -100,6 +105,7 @@ overridden:
|
|||
- "no switchport access vlan"
|
||||
- "no switchport voice vlan"
|
||||
- "interface GigabitEthernet0/2"
|
||||
- "no switchport mode"
|
||||
- "no switchport trunk pruning vlan"
|
||||
- "switchport trunk encapsulation isl"
|
||||
- "switchport trunk native vlan 30"
|
||||
|
@ -124,7 +130,8 @@ deleted:
|
|||
voice:
|
||||
vlan: 40
|
||||
name: GigabitEthernet0/1
|
||||
- name: GigabitEthernet0/2
|
||||
- mode: trunk
|
||||
name: GigabitEthernet0/2
|
||||
trunk:
|
||||
allowed_vlans:
|
||||
- 10-20
|
||||
|
@ -140,6 +147,7 @@ deleted:
|
|||
- "no switchport access vlan"
|
||||
- "no switch voice vlan"
|
||||
- "interface GigabitEthernet0/2"
|
||||
- "no switchport mode"
|
||||
- "no switchport trunk encapsulation"
|
||||
- "no switchport trunk native vlan"
|
||||
- "no switchport trunk allowed vlan"
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
switchport trunk native vlan 10
|
||||
interface {{ test_int2 }}
|
||||
switchport
|
||||
switchport mode trunk
|
||||
switchport trunk allowed vlan 20
|
||||
|
||||
- name: Gather l2_interfaces facts
|
||||
|
@ -43,6 +44,7 @@
|
|||
- "'interface {{ test_int1 }}' in result.commands"
|
||||
- "'no switchport trunk native vlan' in result.commands"
|
||||
- "'interface {{ test_int2 }}' in result.commands"
|
||||
- "'no switchport mode' in result.commands"
|
||||
- "'no switchport trunk allowed vlan' in result.commands"
|
||||
- "result.commands|length == 4"
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
nxos_l2_interfaces: &vlanadd
|
||||
config:
|
||||
- name: "{{ test_int1 }}"
|
||||
mode: trunk
|
||||
trunk:
|
||||
allowed_vlans: "10-12"
|
||||
state: merged
|
||||
|
@ -69,6 +70,7 @@
|
|||
that:
|
||||
- "result.changed == true"
|
||||
- "'interface {{ test_int1 }}' in result.commands"
|
||||
- "'switchport mode trunk' in result.commands"
|
||||
- "'switchport trunk allowed vlan add 10,11,12' in result.commands"
|
||||
- "result.commands|length == 2"
|
||||
|
||||
|
|
|
@ -80,20 +80,24 @@ class TestEosL2InterfacesModule(TestEosModule):
|
|||
set_module_args(dict(
|
||||
config=[dict(
|
||||
name="Ethernet2",
|
||||
mode="trunk",
|
||||
trunk=dict(native_vlan=50)
|
||||
), dict(
|
||||
name="Ethernet3",
|
||||
access=dict(vlan=30),
|
||||
)], state="replaced"
|
||||
))
|
||||
commands = ['interface Ethernet2', 'switchport trunk native vlan 50',
|
||||
'interface Ethernet3', 'switchport access vlan 30']
|
||||
commands = ['interface Ethernet2',
|
||||
'switchport trunk native vlan 50',
|
||||
'interface Ethernet3',
|
||||
'switchport access vlan 30']
|
||||
self.execute_module(changed=True, commands=commands)
|
||||
|
||||
def test_eos_l2_interfaces_replaced_idempotent(self):
|
||||
set_module_args(dict(
|
||||
config=[dict(
|
||||
name="Ethernet2",
|
||||
mode="trunk",
|
||||
trunk=dict(native_vlan=20),
|
||||
), dict(
|
||||
name="Ethernet1",
|
||||
|
@ -106,17 +110,22 @@ class TestEosL2InterfacesModule(TestEosModule):
|
|||
set_module_args(dict(
|
||||
config=[dict(
|
||||
name="Ethernet2",
|
||||
mode="trunk",
|
||||
trunk=dict(native_vlan=50)
|
||||
)], state="overridden"
|
||||
))
|
||||
commands = ['interface Ethernet1', 'no switchport access vlan',
|
||||
'interface Ethernet2', 'switchport trunk native vlan 50']
|
||||
commands = ['interface Ethernet2',
|
||||
'switchport trunk native vlan 50',
|
||||
'interface Ethernet1',
|
||||
'no switchport access vlan'
|
||||
]
|
||||
self.execute_module(changed=True, commands=commands)
|
||||
|
||||
def test_eos_l2_interfaces_overridden_idempotent(self):
|
||||
set_module_args(dict(
|
||||
config=[dict(
|
||||
name="Ethernet2",
|
||||
mode="trunk",
|
||||
trunk=dict(native_vlan=20)
|
||||
), dict(
|
||||
name="Ethernet1",
|
||||
|
@ -129,12 +138,13 @@ class TestEosL2InterfacesModule(TestEosModule):
|
|||
set_module_args(dict(
|
||||
config=[dict(
|
||||
name="Ethernet2",
|
||||
mode="trunk",
|
||||
trunk=dict(native_vlan=20)
|
||||
), dict(
|
||||
name="Ethernet1",
|
||||
access=dict(vlan=20),
|
||||
)], state="deleted"
|
||||
))
|
||||
commands = ['interface Ethernet1', 'no switchport access vlan',
|
||||
'interface Ethernet2', 'no switchport trunk native vlan']
|
||||
commands = ['interface Ethernet2', 'no switchport mode', 'no switchport trunk native vlan',
|
||||
'interface Ethernet1', 'no switchport access vlan']
|
||||
self.execute_module(changed=True, commands=commands)
|
||||
|
|
Loading…
Reference in a new issue