Add Switchport mode support in newly introduced *_l2_interfaces resource module (#67456)

* fixes 65032
This commit is contained in:
Sumit Jaiswal 2020-02-26 23:49:00 +05:30 committed by GitHub
parent a18d3257e9
commit d8c5c6eb9d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 120 additions and 20 deletions

View file

@ -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'}},

View file

@ -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")

View file

@ -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:

View file

@ -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'}}
},

View file

@ -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'):

View file

@ -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)}

View file

@ -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

View file

@ -47,6 +47,10 @@ class L2_interfacesArgs(object): # pylint: disable=R0903
},
'type': 'dict'
},
'mode': {
'type': 'str',
'choices': ['access', 'trunk']
},
'name': {
'required': True,
'type': 'str'

View file

@ -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[:]

View file

@ -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')

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -10,6 +10,7 @@
interface {{ item }}
no switchport mode
no switchport access vlan
no switchport trunk native vlan
with_items:
- Ethernet1
- Ethernet2

View file

@ -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:

View file

@ -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:

View file

@ -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

View file

@ -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"

View file

@ -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"

View file

@ -17,6 +17,7 @@
native_vlan: 20
pruning_vlans: 10,20
encapsulation: dot1q
mode: trunk
state: merged
register: result

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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)