Resource module for ios_vlans (#59640)

* ios_vlans resource module

* fix shippable error

* fix shippable

Signed-off-by: Sumit Jaiswal <sjaiswal@redhat.com>

* fix vlans resource

* fix facts doc

Signed-off-by: Sumit Jaiswal <sjaiswal@redhat.com>

* fix shippable n review

* fix test

Signed-off-by: Sumit Jaiswal <sjaiswal@redhat.com>

* fix indentation

Signed-off-by: Sumit Jaiswal <sjaiswal@redhat.com>

* fix review

* fix shebang

Signed-off-by: Sumit Jaiswal <sjaiswal@redhat.com>
This commit is contained in:
Sumit Jaiswal 2019-08-19 16:03:29 +05:30 committed by GitHub
parent 8e99f0ecc6
commit b55a54469c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 1530 additions and 15 deletions

View file

@ -24,7 +24,9 @@ class FactsArgs(object):
'interfaces', 'interfaces',
'!interfaces', '!interfaces',
'l2_interfaces', 'l2_interfaces',
'!l2_interfaces' '!l2_interfaces',
'vlans',
'!vlans',
] ]
argument_spec = { argument_spec = {

View file

@ -0,0 +1,50 @@
#
# -*- 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)
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the ios_vlans module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class VlansArgs(object):
"""The arg spec for the ios_vlans module
"""
def __init__(self, **kwargs):
pass
argument_spec = {'config': {'elements': 'dict',
'options': {'name': {'type': 'str'},
'vlan_id': {'required': True, 'type': 'int'},
'mtu': {'type': 'int'},
'remote_span': {'type': 'bool'},
'state': {'type': 'str', 'choices': ['active', 'suspend']},
'shutdown': {'type': 'str', 'choices': ['enabled', 'disabled']}},
'type': 'list'},
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
'default': 'merged',
'type': 'str'}}

View file

@ -0,0 +1,277 @@
#
# -*- 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)
"""
The ios_vlans class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible.module_utils.network.common.cfg.base import ConfigBase
from ansible.module_utils.network.common.utils import to_list
from ansible.module_utils.network.ios.facts.facts import Facts
from ansible.module_utils.network.ios.utils.utils import dict_to_set
class Vlans(ConfigBase):
"""
The ios_vlans class
"""
gather_subset = [
'!all',
'!min',
]
gather_network_resources = [
'vlans',
]
def __init__(self, module):
super(Vlans, self).__init__(module)
def get_interfaces_facts(self):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
interfaces_facts = facts['ansible_network_resources'].get('vlans')
if not interfaces_facts:
return []
return interfaces_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {'changed': False}
commands = list()
warnings = list()
existing_interfaces_facts = self.get_interfaces_facts()
commands.extend(self.set_config(existing_interfaces_facts))
if commands:
if not self._module.check_mode:
self._connection.edit_config(commands)
result['changed'] = True
result['commands'] = commands
changed_interfaces_facts = self.get_interfaces_facts()
result['before'] = existing_interfaces_facts
if result['changed']:
result['after'] = changed_interfaces_facts
result['warnings'] = warnings
return result
def set_config(self, existing_interfaces_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
want = self._module.params['config']
have = existing_interfaces_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, want, have):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
state = self._module.params['state']
if state == 'overridden':
commands = self._state_overridden(want, have, state)
elif state == 'deleted':
commands = self._state_deleted(want, have, state)
elif state == 'merged':
commands = self._state_merged(want, have)
elif state == 'replaced':
commands = self._state_replaced(want, have)
return commands
def _state_replaced(self, 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 = []
check = False
for each in want:
for every in have:
if every['vlan_id'] == each['vlan_id']:
check = True
break
else:
continue
if check:
commands.extend(self._set_config(each, every))
else:
commands.extend(self._set_config(each, dict()))
return commands
def _state_overridden(self, want, have, state):
""" 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 each in have:
for every in want:
if each['vlan_id'] == every['vlan_id']:
break
else:
# We didn't find a matching desired state, which means we can
# pretend we recieved an empty desired state.
commands.extend(self._clear_config(every, each, state))
continue
commands.extend(self._set_config(every, each))
return commands
def _state_merged(self, 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 = []
check = False
for each in want:
for every in have:
if each.get('vlan_id') == every.get('vlan_id'):
check = True
break
else:
continue
if check:
commands.extend(self._set_config(each, every))
else:
commands.extend(self._set_config(each, dict()))
return commands
def _state_deleted(self, want, have, state):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
if want:
check = False
for each in want:
for every in have:
if each.get('vlan_id') == every.get('vlan_id'):
check = True
break
else:
check = False
continue
if check:
commands.extend(self._clear_config(each, every, state))
else:
for each in have:
commands.extend(self._clear_config(dict(), each, state))
return commands
def remove_command_from_config_list(self, vlan, cmd, commands):
if vlan not in commands and cmd != 'vlan':
commands.insert(0, vlan)
elif cmd == 'vlan':
commands.append('no %s' % vlan)
return commands
commands.append('no %s' % cmd)
return commands
def add_command_to_config_list(self, vlan_id, cmd, commands):
if vlan_id not in commands:
commands.insert(0, vlan_id)
if cmd not in commands:
commands.append(cmd)
def _set_config(self, want, have):
# Set the interface config based on the want and have config
commands = []
vlan = 'vlan {0}'.format(want.get('vlan_id'))
# Get the diff b/w want n have
want_dict = dict_to_set(want)
have_dict = dict_to_set(have)
diff = want_dict - have_dict
if diff:
name = dict(diff).get('name')
state = dict(diff).get('state')
shutdown = dict(diff).get('shutdown')
mtu = dict(diff).get('mtu')
remote_span = dict(diff).get('remote_span')
if name:
cmd = 'name {0}'.format(name)
self.add_command_to_config_list(vlan, cmd, commands)
if state:
cmd = 'state {0}'.format(state)
self.add_command_to_config_list(vlan, cmd, commands)
if mtu:
cmd = 'mtu {0}'.format(mtu)
self.add_command_to_config_list(vlan, cmd, commands)
if remote_span:
self.add_command_to_config_list(vlan, 'remote-span', commands)
if shutdown == 'enabled':
self.add_command_to_config_list(vlan, 'shutdown', commands)
elif shutdown == 'disabled':
self.add_command_to_config_list(vlan, 'no shutdown', commands)
return commands
def _clear_config(self, want, have, state):
# Delete the interface config based on the want and have config
commands = []
vlan = 'vlan {0}'.format(have.get('vlan_id'))
if have.get('vlan_id') and 'default' not in have.get('name')\
and (have.get('vlan_id') != want.get('vlan_id') or state == 'deleted'):
self.remove_command_from_config_list(vlan, 'vlan', commands)
elif 'default' not in have.get('name'):
if have.get('mtu') != want.get('mtu'):
self.remove_command_from_config_list(vlan, 'mtu', commands)
if have.get('remote_span') != want.get('remote_span') and want.get('remote_span'):
self.remove_command_from_config_list(vlan, 'remote-span', commands)
if have.get('shutdown') != want.get('shutdown') and want.get('shutdown'):
self.remove_command_from_config_list(vlan, 'shutdown', commands)
if have.get('state') != want.get('state') and want.get('state'):
self.remove_command_from_config_list(vlan, 'state', commands)
return commands

View file

@ -17,6 +17,7 @@ from ansible.module_utils.network.ios.argspec.facts.facts import FactsArgs
from ansible.module_utils.network.common.facts.facts import FactsBase from ansible.module_utils.network.common.facts.facts import FactsBase
from ansible.module_utils.network.ios.facts.interfaces.interfaces import InterfacesFacts from ansible.module_utils.network.ios.facts.interfaces.interfaces import InterfacesFacts
from ansible.module_utils.network.ios.facts.l2_interfaces.l2_interfaces import L2_InterfacesFacts from ansible.module_utils.network.ios.facts.l2_interfaces.l2_interfaces import L2_InterfacesFacts
from ansible.module_utils.network.ios.facts.vlans.vlans import VlansFacts
from ansible.module_utils.network.ios.facts.legacy.base import Default, Hardware, Interfaces, Config from ansible.module_utils.network.ios.facts.legacy.base import Default, Hardware, Interfaces, Config
@ -30,6 +31,7 @@ FACT_LEGACY_SUBSETS = dict(
FACT_RESOURCE_SUBSETS = dict( FACT_RESOURCE_SUBSETS = dict(
interfaces=InterfacesFacts, interfaces=InterfacesFacts,
l2_interfaces=L2_InterfacesFacts, l2_interfaces=L2_InterfacesFacts,
vlans=VlansFacts
) )

View file

@ -0,0 +1,144 @@
#
# -*- 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)
"""
The ios vlans fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from copy import deepcopy
from ansible.module_utils.network.common import utils
from ansible.module_utils.network.ios.argspec.vlans.vlans import VlansArgs
class VlansFacts(object):
""" The ios vlans fact class
"""
def __init__(self, module, subspec='config', options='options'):
self._module = module
self.argument_spec = VlansArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for vlans
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if connection:
pass
objs = []
mtu_objs = []
remote_objs = []
final_objs = []
if not data:
data = connection.get('show vlan')
# operate on a collection of resource x
config = data.split('\n')
# Get individual vlan configs separately
vlan_info = ''
for conf in config:
if 'Name' in conf:
vlan_info = 'Name'
elif 'Type' in conf:
vlan_info = 'Type'
elif 'Remote' in conf:
vlan_info = 'Remote'
if conf and ' ' not in filter(None, conf.split('-')):
obj = self.render_config(self.generated_spec, conf, vlan_info)
if 'mtu' in obj:
mtu_objs.append(obj)
elif 'remote_span' in obj:
remote_objs = obj
elif obj:
objs.append(obj)
# Appending MTU value to the retrieved dictionary
for o, m in zip(objs, mtu_objs):
o.update(m)
final_objs.append(o)
# Appending Remote Span value to related VLAN:
if remote_objs:
if remote_objs.get('remote_span'):
for each in remote_objs.get('remote_span'):
for every in final_objs:
if each == every.get('vlan_id'):
every.update({'remote_span': True})
break
facts = {}
if final_objs:
facts['vlans'] = []
params = utils.validate_config(self.argument_spec, {'config': objs})
for cfg in params['config']:
facts['vlans'].append(utils.remove_empties(cfg))
ansible_facts['ansible_network_resources'].update(facts)
return ansible_facts
def render_config(self, spec, conf, vlan_info):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
config = deepcopy(spec)
if vlan_info == 'Name' and 'Name' not in conf:
conf = filter(None, conf.split(' '))
config['vlan_id'] = int(conf[0])
config['name'] = conf[1]
if len(conf[2].split('/')) > 1:
if conf[2].split('/')[0] == 'sus':
config['state'] = 'suspend'
elif conf[2].split('/')[0] == 'act':
config['state'] = 'active'
config['shutdown'] = 'enabled'
else:
if conf[2] == 'suspended':
config['state'] = 'suspend'
elif conf[2] == 'active':
config['state'] = 'active'
config['shutdown'] = 'disabled'
elif vlan_info == 'Type' and 'Type' not in conf:
conf = filter(None, conf.split(' '))
config['mtu'] = int(conf[3])
elif vlan_info == 'Remote':
if len(conf.split(',')) > 1 or conf.isdigit():
remote_span_vlan = []
if len(conf.split(',')) > 1:
remote_span_vlan = conf.split(',')
else:
remote_span_vlan.append(conf)
remote_span = []
for each in remote_span_vlan:
remote_span.append(int(each))
config['remote_span'] = remote_span
return utils.remove_empties(config)

View file

@ -39,7 +39,7 @@ def dict_to_set(sample_dict):
for key, value in iteritems(each): for key, value in iteritems(each):
if isinstance(value, list): if isinstance(value, list):
each[key] = tuple(value) each[key] = tuple(value)
li.extend(tuple(each.items())) li.append(tuple(each.items()))
v = tuple(li) v = tuple(li)
else: else:
v = tuple(v) v = tuple(v)

View file

@ -9,7 +9,7 @@ __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['deprecated'],
'supported_by': 'network'} 'supported_by': 'network'}
DOCUMENTATION = """ DOCUMENTATION = """
@ -21,6 +21,10 @@ short_description: Manage VLANs on IOS network devices
description: description:
- This module provides declarative management of VLANs - This module provides declarative management of VLANs
on Cisco IOS network devices. on Cisco IOS network devices.
deprecated:
removed_in: '2.13'
alternative: ios_vlans
why: Newer and updated modules released with more functionality in Ansible 2.9
notes: notes:
- Tested against IOS 15.2 - Tested against IOS 15.2
options: options:

View file

@ -54,7 +54,7 @@ options:
to a given subset. Possible values for this argument include to a given subset. Possible values for this argument include
all and the resources like interfaces, vlans etc. all and the resources like interfaces, vlans etc.
Can specify a list of values to include a larger subset. Can specify a list of values to include a larger subset.
choices: ['all', '!all', 'interfaces', '!interfaces', 'l2_interfaces', '!l2_interfaces'] choices: ['all', '!all', 'interfaces', '!interfaces', 'l2_interfaces', '!l2_interfaces', 'vlans', '!vlans']
version_added: "2.9" version_added: "2.9"
""" """

View file

@ -0,0 +1,459 @@
#!/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)
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The module file for ios_vlans
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'
}
DOCUMENTATION = """
module: ios_vlans
version_added: 2.9
short_description: Manage VLANs on Cisco IOS devices.
description: This module provides declarative management of VLANs on Cisco IOS network devices.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOSv Version 15.2 on VIRL
- This module works with connection C(network_cli).
See L(IOS Platform Options,../network/user_guide/platform_ios.html).
options:
config:
description: A dictionary of VLANs options
type: list
elements: dict
suboptions:
name:
description:
- Ascii name of the VLAN.
- NOTE, I(name) should not be named/appended with I(default) as it is reserved for device default vlans.
type: str
vlan_id:
description:
- ID of the VLAN. Range 1-4094
type: int
required: True
mtu:
description:
- VLAN Maximum Transmission Unit.
- Refer to vendor documentation for valid values.
type: int
state:
description:
- Operational state of the VLAN
type: str
choices:
- active
- suspend
remote_span:
description:
- Configure as Remote SPAN VLAN
type: bool
shutdown:
description:
- Shutdown VLAN switching.
type: str
choices:
- enabled
- disabled
state:
description:
- The state the configuration should be left in
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
"""
EXAMPLES = """
---
# Using merged
# Before state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
- name: Merge provided configuration with device configuration
ios_vlans:
config:
- name: Vlan_10
vlan_id: 10
state: active
shutdown: disabled
remote_span: 10
- name: Vlan_20
vlan_id: 20
mtu: 610
state: active
shutdown: enabled
- name: Vlan_30
vlan_id: 30
state: suspend
shutdown: enabled
state: merged
# After state:
# ------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 10 vlan_10 active
# 20 vlan_20 act/lshut
# 30 vlan_30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 10 enet 100010 1500 - - - - - 0 0
# 20 enet 100020 610 - - - - - 0 0
# 30 enet 100030 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
#
# Remote SPAN VLANs
# ------------------------------------------------------------------------------
# 10
# Using overridden
# Before state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 10 vlan_10 active
# 20 vlan_20 act/lshut
# 30 vlan_30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 10 enet 100010 1500 - - - - - 0 0
# 20 enet 100020 610 - - - - - 0 0
# 30 enet 100030 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
#
# Remote SPAN VLANs
# ------------------------------------------------------------------------------
# 10
- name: Override device configuration of all VLANs with provided configuration
ios_vlans:
config:
- name: Vlan_10
vlan_id: 10
mtu: 1000
state: overridden
# After state:
# ------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 10 Vlan_10 active
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 10 enet 100010 1000 - - - - - 0 0
# Using replaced
# Before state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 10 vlan_10 active
# 20 vlan_20 act/lshut
# 30 vlan_30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 10 enet 100010 1500 - - - - - 0 0
# 20 enet 100020 610 - - - - - 0 0
# 30 enet 100030 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
#
# Remote SPAN VLANs
# ------------------------------------------------------------------------------
# 10
- name: Replaces device configuration of listed VLANs with provided configuration
ios_vlans:
config:
- vlan_id: 20
name: Test_VLAN20
mtu: 700
shutdown: disabled
- vlan_id: 30
name: Test_VLAN30
mtu: 1000
state: replaced
# After state:
# ------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 10 vlan_10 active
# 20 Test_VLAN20 active
# 30 Test_VLAN30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 10 enet 100010 1500 - - - - - 0 0
# 20 enet 100020 700 - - - - - 0 0
# 30 enet 100030 1000 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
#
# Remote SPAN VLANs
# ------------------------------------------------------------------------------
# 10
# Using deleted
# Before state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 10 vlan_10 active
# 20 vlan_20 act/lshut
# 30 vlan_30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 10 enet 100010 1500 - - - - - 0 0
# 20 enet 100020 610 - - - - - 0 0
# 30 enet 100030 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
#
# Remote SPAN VLANs
# ------------------------------------------------------------------------------
# 10
- name: Delete attributes of given VLANs
ios_vlans:
config:
- vlan_id: 10
- vlan_id: 20
state: deleted
# After state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 30 vlan_30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 30 enet 100030 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
# Using Deleted without any config passed
#"(NOTE: This will delete all of configured vlans attributes)"
# Before state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 10 vlan_10 active
# 20 vlan_20 act/lshut
# 30 vlan_30 sus/lshut
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 10 enet 100010 1500 - - - - - 0 0
# 20 enet 100020 610 - - - - - 0 0
# 30 enet 100030 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
#
# Remote SPAN VLANs
# ------------------------------------------------------------------------------
# 10
- name: Delete attributes of ALL VLANs
ios_vlans:
state: deleted
# After state:
# -------------
#
# vios#show vlan
# VLAN Name Status Ports
# ---- -------------------------------- --------- -------------------------------
# 1 default active Gi0/1, Gi0/2
# 1002 fddi-default act/unsup
# 1003 token-ring-default act/unsup
# 1004 fddinet-default act/unsup
# 1005 trnet-default act/unsup
#
# VLAN Type SAID MTU Parent RingNo BridgeNo Stp BrdgMode Trans1 Trans2
# ---- ----- ---------- ----- ------ ------ -------- ---- -------- ------ ------
# 1 enet 100001 1500 - - - - - 0 0
# 1002 fddi 101002 1500 - - - - - 0 0
# 1003 tr 101003 1500 - - - - - 0 0
# 1004 fdnet 101004 1500 - - - ieee - 0 0
# 1005 trnet 101005 1500 - - - ibm - 0 0
"""
RETURN = """
before:
description: The configuration prior to the model invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The resulting configuration model invocation.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample: ['vlan 20', 'name vlan_20', 'mtu 600', 'remote-span']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.vlans.vlans import VlansArgs
from ansible.module_utils.network.ios.config.vlans.vlans import Vlans
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
module = AnsibleModule(argument_spec=VlansArgs.argument_spec,
supports_check_mode=True)
result = Vlans(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,3 @@
---
testcase: "[^_].*"
test_items: []

View file

@ -0,0 +1 @@
dependencies: []

View file

@ -0,0 +1,24 @@
---
- name: Collect all cli test cases
find:
paths: "{{ role_path }}/tests/cli"
patterns: "{{ testcase }}.yaml"
use_regex: true
register: test_cases
delegate_to: localhost
- name: Set test_items
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
delegate_to: localhost
- name: Get the IOS version
ios_facts:
gather_subset: all
- name: Run test case (connection=network_cli)
include: "{{ test_case_to_run }}"
vars:
ansible_connection: network_cli
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

View file

@ -0,0 +1,2 @@
---
- { include: cli.yaml, tags: ['cli'] }

View file

@ -0,0 +1,10 @@
---
- name: Setup
cli_config:
config: "{{ lines }}"
vars:
lines: |
vlan 10
vlan 20
vlan 30
when: ansible_net_version != "15.6(2)T"

View file

@ -0,0 +1,10 @@
---
- name: Remove Config
cli_config:
config: "{{ lines }}"
vars:
lines: |
no vlan 10
no vlan 20
no vlan 30
when: ansible_net_version != "15.6(2)T"

View file

@ -0,0 +1,42 @@
---
- debug:
msg: "Start Deleted integration state for ios_vlans ansible_connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate_config.yaml
- block:
- name: Delete VLANs attributes for all configured VLANs
ios_vlans: &deleted
state: deleted
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ deleted['commands'] | symmetric_difference(result['commands']) | length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ deleted['before'] | symmetric_difference(result['before']) | length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ deleted['after'] | symmetric_difference(result['after']) | length == 0 }}"
- name: Delete VLANs attributes for all configured VLANs (IDEMPOTENT)
ios_vlans: *deleted
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result.changed == false"
when: ansible_net_version != "15.6(2)T"
always:
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,54 @@
---
- debug:
msg: "START Merged ios_vlans state for integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- block:
- name: Merge provided configuration with device configuration
ios_vlans: &merged
config:
- name: Vlan_10
vlan_id: 10
state: active
shutdown: disabled
remote_span: 10
- name: Vlan_20
vlan_id: 20
mtu: 610
state: active
shutdown: enabled
- name: Vlan_30
vlan_id: 30
state: suspend
shutdown: enabled
state: merged
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ merged['commands'] | symmetric_difference(result['commands']) | length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ merged['before'] | symmetric_difference(result['before']) | length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['after']) | length == 0 }}"
- name: Merge provided configuration with device configuration (IDEMPOTENT)
ios_vlans: *merged
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
when: ansible_net_version != "15.6(2)T"
always:
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,45 @@
---
- debug:
msg: "START Overridden ios_vlans state for integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate_config.yaml
- block:
- name: Override device configuration of all VLANs with provided configuration
ios_vlans: &overridden
config:
- name: VLAN_10
vlan_id: 10
mtu: 1000
state: overridden
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ overridden['commands'] | symmetric_difference(result['commands']) | length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ overridden['before'] | symmetric_difference(result['before']) | length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ overridden['after'] | symmetric_difference(result['after']) | length == 0 }}"
- name: Override device configuration of all interfaces with provided configuration (IDEMPOTENT)
ios_vlans: *overridden
register: result
- name: Assert that task was idempotent
assert:
that:
- "result['changed'] == false"
when: ansible_net_version != "15.6(2)T"
always:
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,49 @@
---
- debug:
msg: "START Replaced ios_vlans state for integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate_config.yaml
- block:
- name: Replaces device configuration of listed VLANs with provided configuration
ios_vlans: &replaced
config:
- vlan_id: 20
name: Test_VLAN20
mtu: 700
shutdown: disabled
- vlan_id: 30
name: Test_VLAN30
mtu: 1000
state: replaced
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ replaced['commands'] | symmetric_difference(result['commands']) | length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ replaced['before'] | symmetric_difference(result['before']) | length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ replaced['after'] | symmetric_difference(result['after']) | length == 0 }}"
- name: Replaces device configuration of listed interfaces with provided configuration (IDEMPOTENT)
ios_vlans: *replaced
register: result
- name: Assert that task was idempotent
assert:
that:
- "result['changed'] == false"
when: ansible_net_version != "15.6(2)T"
always:
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,337 @@
---
merged:
before:
- mtu: 1500
name: default
shutdown: disabled
state: active
vlan_id: 1
- mtu: 1500
name: fddi-default
shutdown: enabled
state: active
vlan_id: 1002
- mtu: 1500
name: token-ring-default
shutdown: enabled
state: active
vlan_id: 1003
- mtu: 1500
name: fddinet-default
shutdown: enabled
state: active
vlan_id: 1004
- mtu: 1500
name: trnet-default
shutdown: enabled
state: active
vlan_id: 1005
commands:
- "vlan 10"
- "name Vlan_10"
- "state active"
- "remote-span"
- "no shutdown"
- "vlan 20"
- "name Vlan_20"
- "state active"
- "mtu 610"
- "shutdown"
- "vlan 30"
- "name Vlan_30"
- "state suspend"
- "shutdown"
after:
- mtu: 1500
name: default
shutdown: disabled
state: active
vlan_id: 1
- mtu: 1500
name: Vlan_10
remote_span: true
shutdown: disabled
state: active
vlan_id: 10
- mtu: 610
name: Vlan_20
shutdown: enabled
state: active
vlan_id: 20
- mtu: 1500
name: Vlan_30
shutdown: enabled
state: suspend
vlan_id: 30
- mtu: 1500
name: fddi-default
shutdown: enabled
state: active
vlan_id: 1002
- mtu: 1500
name: token-ring-default
shutdown: enabled
state: active
vlan_id: 1003
- mtu: 1500
name: fddinet-default
shutdown: enabled
state: active
vlan_id: 1004
- mtu: 1500
name: trnet-default
shutdown: enabled
state: active
vlan_id: 1005
replaced:
before:
- mtu: 1500
name: default
shutdown: disabled
state: active
vlan_id: 1
- mtu: 1500
name: VLAN0010
shutdown: disabled
state: active
vlan_id: 10
- mtu: 1500
name: VLAN0020
shutdown: disabled
state: active
vlan_id: 20
- mtu: 1500
name: VLAN0030
shutdown: disabled
state: active
vlan_id: 30
- mtu: 1500
name: fddi-default
shutdown: enabled
state: active
vlan_id: 1002
- mtu: 1500
name: token-ring-default
shutdown: enabled
state: active
vlan_id: 1003
- mtu: 1500
name: fddinet-default
shutdown: enabled
state: active
vlan_id: 1004
- mtu: 1500
name: trnet-default
shutdown: enabled
state: active
vlan_id: 1005
commands:
- "vlan 20"
- "name Test_VLAN20"
- "mtu 700"
- "vlan 30"
- "name Test_VLAN30"
- "mtu 1000"
after:
- mtu: 1500
name: default
shutdown: disabled
state: active
vlan_id: 1
- mtu: 1500
name: VLAN0010
shutdown: disabled
state: active
vlan_id: 10
- mtu: 700
name: Test_VLAN20
shutdown: disabled
state: active
vlan_id: 20
- mtu: 1000
name: Test_VLAN30
shutdown: disabled
state: active
vlan_id: 30
- mtu: 1500
name: fddi-default
shutdown: enabled
state: active
vlan_id: 1002
- mtu: 1500
name: token-ring-default
shutdown: enabled
state: active
vlan_id: 1003
- mtu: 1500
name: fddinet-default
shutdown: enabled
state: active
vlan_id: 1004
- mtu: 1500
name: trnet-default
shutdown: enabled
state: active
vlan_id: 1005
overridden:
before:
- mtu: 1500
name: default
shutdown: disabled
state: active
vlan_id: 1
- mtu: 1500
name: VLAN0010
shutdown: disabled
state: active
vlan_id: 10
- mtu: 1500
name: VLAN0020
shutdown: disabled
state: active
vlan_id: 20
- mtu: 1500
name: VLAN0030
shutdown: disabled
state: active
vlan_id: 30
- mtu: 1500
name: fddi-default
shutdown: enabled
state: active
vlan_id: 1002
- mtu: 1500
name: token-ring-default
shutdown: enabled
state: active
vlan_id: 1003
- mtu: 1500
name: fddinet-default
shutdown: enabled
state: active
vlan_id: 1004
- mtu: 1500
name: trnet-default
shutdown: enabled
state: active
vlan_id: 1005
commands:
- "vlan 10"
- "name VLAN_10"
- "mtu 1000"
- "no vlan 20"
- "no vlan 30"
after:
- mtu: 1500
name: default
shutdown: disabled
state: active
vlan_id: 1
- mtu: 1000
name: VLAN_10
shutdown: disabled
state: active
vlan_id: 10
- mtu: 1500
name: fddi-default
shutdown: enabled
state: active
vlan_id: 1002
- mtu: 1500
name: token-ring-default
shutdown: enabled
state: active
vlan_id: 1003
- mtu: 1500
name: fddinet-default
shutdown: enabled
state: active
vlan_id: 1004
- mtu: 1500
name: trnet-default
shutdown: enabled
state: active
vlan_id: 1005
deleted:
before:
- mtu: 1500
name: default
shutdown: disabled
state: active
vlan_id: 1
- mtu: 1500
name: VLAN0010
shutdown: disabled
state: active
vlan_id: 10
- mtu: 1500
name: VLAN0020
shutdown: disabled
state: active
vlan_id: 20
- mtu: 1500
name: VLAN0030
shutdown: disabled
state: active
vlan_id: 30
- mtu: 1500
name: fddi-default
shutdown: enabled
state: active
vlan_id: 1002
- mtu: 1500
name: token-ring-default
shutdown: enabled
state: active
vlan_id: 1003
- mtu: 1500
name: fddinet-default
shutdown: enabled
state: active
vlan_id: 1004
- mtu: 1500
name: trnet-default
shutdown: enabled
state: active
vlan_id: 1005
commands:
- "no vlan 10"
- "no vlan 20"
- "no vlan 30"
after:
- mtu: 1500
name: default
shutdown: disabled
state: active
vlan_id: 1
- mtu: 1500
name: fddi-default
shutdown: enabled
state: active
vlan_id: 1002
- mtu: 1500
name: token-ring-default
shutdown: enabled
state: active
vlan_id: 1003
- mtu: 1500
name: fddinet-default
shutdown: enabled
state: active
vlan_id: 1004
- mtu: 1500
name: trnet-default
shutdown: enabled
state: active
vlan_id: 1005

View file

@ -4164,12 +4164,12 @@ lib/ansible/modules/network/ios/ios_user.py validate-modules:E326
lib/ansible/modules/network/ios/ios_user.py validate-modules:E337 lib/ansible/modules/network/ios/ios_user.py validate-modules:E337
lib/ansible/modules/network/ios/ios_user.py validate-modules:E338 lib/ansible/modules/network/ios/ios_user.py validate-modules:E338
lib/ansible/modules/network/ios/ios_user.py validate-modules:E340 lib/ansible/modules/network/ios/ios_user.py validate-modules:E340
lib/ansible/modules/network/ios/ios_vlan.py validate-modules:E322 lib/ansible/modules/network/ios/_ios_vlan.py validate-modules:E322
lib/ansible/modules/network/ios/ios_vlan.py validate-modules:E324 lib/ansible/modules/network/ios/_ios_vlan.py validate-modules:E324
lib/ansible/modules/network/ios/ios_vlan.py validate-modules:E326 lib/ansible/modules/network/ios/_ios_vlan.py validate-modules:E326
lib/ansible/modules/network/ios/ios_vlan.py validate-modules:E337 lib/ansible/modules/network/ios/_ios_vlan.py validate-modules:E337
lib/ansible/modules/network/ios/ios_vlan.py validate-modules:E338 lib/ansible/modules/network/ios/_ios_vlan.py validate-modules:E338
lib/ansible/modules/network/ios/ios_vlan.py validate-modules:E340 lib/ansible/modules/network/ios/_ios_vlan.py validate-modules:E340
lib/ansible/modules/network/ios/ios_vrf.py future-import-boilerplate lib/ansible/modules/network/ios/ios_vrf.py future-import-boilerplate
lib/ansible/modules/network/ios/ios_vrf.py metaclass-boilerplate lib/ansible/modules/network/ios/ios_vrf.py metaclass-boilerplate
lib/ansible/modules/network/ios/ios_vrf.py validate-modules:E324 lib/ansible/modules/network/ios/ios_vrf.py validate-modules:E324

View file

@ -20,23 +20,23 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
from units.compat.mock import patch from units.compat.mock import patch
from ansible.modules.network.ios import ios_vlan from ansible.modules.network.ios import _ios_vlan
from ansible.modules.network.ios.ios_vlan import parse_vlan_brief from ansible.modules.network.ios._ios_vlan import parse_vlan_brief
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
from .ios_module import TestIosModule, load_fixture from .ios_module import TestIosModule, load_fixture
class TestIosVlanModule(TestIosModule): class TestIosVlanModule(TestIosModule):
module = ios_vlan module = _ios_vlan
def setUp(self): def setUp(self):
super(TestIosVlanModule, self).setUp() super(TestIosVlanModule, self).setUp()
self.mock_run_commands = patch('ansible.modules.network.ios.ios_vlan.run_commands') self.mock_run_commands = patch('ansible.modules.network.ios._ios_vlan.run_commands')
self.run_commands = self.mock_run_commands.start() self.run_commands = self.mock_run_commands.start()
self.mock_load_config = patch('ansible.modules.network.ios.ios_vlan.load_config') self.mock_load_config = patch('ansible.modules.network.ios._ios_vlan.load_config')
self.load_config = self.mock_load_config.start() self.load_config = self.mock_load_config.start()
def tearDown(self): def tearDown(self):