mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-20 18:59:08 +00:00
Initial commit
This commit is contained in:
commit
aebc1b03fd
4861 changed files with 812621 additions and 0 deletions
270
plugins/modules/network/edgeswitch/edgeswitch_facts.py
Normal file
270
plugins/modules/network/edgeswitch/edgeswitch_facts.py
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: edgeswitch_facts
|
||||
author: "Frederic Bor (@f-bor)"
|
||||
short_description: Collect facts from remote devices running Edgeswitch
|
||||
description:
|
||||
- Collects a base set of device facts from a remote device that
|
||||
is running Ubiquiti Edgeswitch. This module prepends all of the
|
||||
base network fact keys with C(ansible_net_<fact>). The facts
|
||||
module will always collect a base set of facts from the device
|
||||
and can enable or disable collection of additional facts.
|
||||
notes:
|
||||
- Tested against Edgeswitch 1.7.4
|
||||
options:
|
||||
gather_subset:
|
||||
description:
|
||||
- When supplied, this argument will restrict the facts collected
|
||||
to a given subset. Possible values for this argument include
|
||||
all, config, and interfaces. Can specify a list of
|
||||
values to include a larger subset. Values can also be used
|
||||
with an initial C(M(!)) to specify that a specific subset should
|
||||
not be collected.
|
||||
required: false
|
||||
default: '!config'
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
# Collect all facts from the device
|
||||
- edgeswitch_facts:
|
||||
gather_subset: all
|
||||
|
||||
# Collect only the config and default facts
|
||||
- edgeswitch_facts:
|
||||
gather_subset:
|
||||
- config
|
||||
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
ansible_net_gather_subset:
|
||||
description: The list of fact subsets collected from the device
|
||||
returned: always
|
||||
type: list
|
||||
|
||||
# default
|
||||
ansible_net_model:
|
||||
description: The model name returned from the device
|
||||
returned: always
|
||||
type: str
|
||||
ansible_net_serialnum:
|
||||
description: The serial number of the remote device
|
||||
returned: always
|
||||
type: str
|
||||
ansible_net_version:
|
||||
description: The operating system version running on the remote device
|
||||
returned: always
|
||||
type: str
|
||||
ansible_net_hostname:
|
||||
description: The configured hostname of the device
|
||||
returned: always
|
||||
type: str
|
||||
|
||||
# config
|
||||
ansible_net_config:
|
||||
description: The current active config from the device
|
||||
returned: when config is configured
|
||||
type: str
|
||||
|
||||
# interfaces
|
||||
ansible_net_interfaces:
|
||||
description: A hash of all interfaces running on the system
|
||||
returned: when interfaces is configured
|
||||
type: dict
|
||||
"""
|
||||
import re
|
||||
|
||||
from ansible_collections.community.general.plugins.module_utils.network.edgeswitch.edgeswitch import run_commands
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
|
||||
class FactsBase(object):
|
||||
|
||||
COMMANDS = list()
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.facts = dict()
|
||||
self.responses = None
|
||||
|
||||
def populate(self):
|
||||
self.responses = run_commands(self.module, commands=self.COMMANDS, check_rc=False)
|
||||
|
||||
def run(self, cmd):
|
||||
return run_commands(self.module, commands=cmd, check_rc=False)
|
||||
|
||||
|
||||
class Default(FactsBase):
|
||||
|
||||
COMMANDS = ['show version', 'show sysinfo']
|
||||
|
||||
def populate(self):
|
||||
super(Default, self).populate()
|
||||
data = self.responses[0]
|
||||
if data:
|
||||
self.facts['version'] = self.parse_version(data)
|
||||
self.facts['serialnum'] = self.parse_serialnum(data)
|
||||
self.facts['model'] = self.parse_model(data)
|
||||
self.facts['hostname'] = self.parse_hostname(self.responses[1])
|
||||
|
||||
def parse_version(self, data):
|
||||
match = re.search(r'Software Version\.+ (.*)', data)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
def parse_hostname(self, data):
|
||||
match = re.search(r'System Name\.+ (.*)', data)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
def parse_model(self, data):
|
||||
match = re.search(r'Machine Model\.+ (.*)', data)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
def parse_serialnum(self, data):
|
||||
match = re.search(r'Serial Number\.+ (.*)', data)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
|
||||
class Config(FactsBase):
|
||||
|
||||
COMMANDS = ['show running-config']
|
||||
|
||||
def populate(self):
|
||||
super(Config, self).populate()
|
||||
data = self.responses[0]
|
||||
if data:
|
||||
self.facts['config'] = data
|
||||
|
||||
|
||||
class Interfaces(FactsBase):
|
||||
|
||||
COMMANDS = [
|
||||
'show interfaces description',
|
||||
'show interfaces status all'
|
||||
]
|
||||
|
||||
def populate(self):
|
||||
super(Interfaces, self).populate()
|
||||
|
||||
interfaces = {}
|
||||
|
||||
data = self.responses[0]
|
||||
self.parse_interfaces_description(data, interfaces)
|
||||
|
||||
data = self.responses[1]
|
||||
self.parse_interfaces_status(data, interfaces)
|
||||
|
||||
self.facts['interfaces'] = interfaces
|
||||
|
||||
def parse_interfaces_description(self, data, interfaces):
|
||||
for line in data.split('\n'):
|
||||
match = re.match(r'(\d\/\d+)\s+(\w+)\s+(\w+)', line)
|
||||
if match:
|
||||
name = match.group(1)
|
||||
interface = {}
|
||||
interface['operstatus'] = match.group(2)
|
||||
interface['lineprotocol'] = match.group(3)
|
||||
interface['description'] = line[30:]
|
||||
interfaces[name] = interface
|
||||
|
||||
def parse_interfaces_status(self, data, interfaces):
|
||||
for line in data.split('\n'):
|
||||
match = re.match(r'(\d\/\d+)', line)
|
||||
if match:
|
||||
name = match.group(1)
|
||||
interface = interfaces[name]
|
||||
interface['physicalstatus'] = line[61:71].strip()
|
||||
interface['mediatype'] = line[73:91].strip()
|
||||
|
||||
|
||||
FACT_SUBSETS = dict(
|
||||
default=Default,
|
||||
config=Config,
|
||||
interfaces=Interfaces,
|
||||
)
|
||||
|
||||
VALID_SUBSETS = frozenset(FACT_SUBSETS.keys())
|
||||
|
||||
|
||||
def main():
|
||||
"""main entry point for module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
gather_subset=dict(default=['!config'], type='list')
|
||||
)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
gather_subset = module.params['gather_subset']
|
||||
|
||||
runable_subsets = set()
|
||||
exclude_subsets = set()
|
||||
|
||||
for subset in gather_subset:
|
||||
if subset == 'all':
|
||||
runable_subsets.update(VALID_SUBSETS)
|
||||
continue
|
||||
|
||||
if subset.startswith('!'):
|
||||
subset = subset[1:]
|
||||
if subset == 'all':
|
||||
exclude_subsets.update(VALID_SUBSETS)
|
||||
continue
|
||||
exclude = True
|
||||
else:
|
||||
exclude = False
|
||||
|
||||
if subset not in VALID_SUBSETS:
|
||||
module.fail_json(msg='Bad subset')
|
||||
|
||||
if exclude:
|
||||
exclude_subsets.add(subset)
|
||||
else:
|
||||
runable_subsets.add(subset)
|
||||
|
||||
if not runable_subsets:
|
||||
runable_subsets.update(VALID_SUBSETS)
|
||||
|
||||
runable_subsets.difference_update(exclude_subsets)
|
||||
runable_subsets.add('default')
|
||||
|
||||
facts = dict()
|
||||
facts['gather_subset'] = list(runable_subsets)
|
||||
|
||||
instances = list()
|
||||
for key in runable_subsets:
|
||||
instances.append(FACT_SUBSETS[key](module))
|
||||
|
||||
for inst in instances:
|
||||
inst.populate()
|
||||
facts.update(inst.facts)
|
||||
|
||||
ansible_facts = dict()
|
||||
for key, value in iteritems(facts):
|
||||
key = 'ansible_net_%s' % key
|
||||
ansible_facts[key] = value
|
||||
|
||||
module.exit_json(ansible_facts=ansible_facts)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
497
plugins/modules/network/edgeswitch/edgeswitch_vlan.py
Normal file
497
plugins/modules/network/edgeswitch/edgeswitch_vlan.py
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2018, Ansible by Red Hat, inc
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: edgeswitch_vlan
|
||||
author: "Frederic Bor (@f-bor)"
|
||||
short_description: Manage VLANs on Ubiquiti Edgeswitch network devices
|
||||
description:
|
||||
- This module provides declarative management of VLANs
|
||||
on Ubiquiti Edgeswitch network devices.
|
||||
notes:
|
||||
- Tested against edgeswitch 1.7.4
|
||||
- This module use native Ubiquiti vlan syntax and does not support switchport compatibility syntax.
|
||||
For clarity, it is strongly advised to not use both syntaxes on the same interface.
|
||||
- Edgeswitch does not support deleting or changing name of VLAN 1
|
||||
- As auto_tag, auto_untag and auto_exclude are a kind of default setting for all interfaces, they are mutually exclusive
|
||||
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the VLAN.
|
||||
vlan_id:
|
||||
description:
|
||||
- ID of the VLAN. Range 1-4093.
|
||||
tagged_interfaces:
|
||||
description:
|
||||
- List of interfaces that should accept and transmit tagged frames for the VLAN.
|
||||
Accept range of interfaces.
|
||||
untagged_interfaces:
|
||||
description:
|
||||
- List of interfaces that should accept untagged frames and transmit them tagged for the VLAN.
|
||||
Accept range of interfaces.
|
||||
excluded_interfaces:
|
||||
description:
|
||||
- List of interfaces that should be excluded of the VLAN.
|
||||
Accept range of interfaces.
|
||||
auto_tag:
|
||||
description:
|
||||
- Each of the switch interfaces will be set to accept and transmit
|
||||
untagged frames for I(vlan_id) unless defined in I(*_interfaces).
|
||||
This is a default setting for all switch interfaces.
|
||||
type: bool
|
||||
auto_untag:
|
||||
description:
|
||||
- Each of the switch interfaces will be set to accept untagged frames and
|
||||
transmit them tagged for I(vlan_id) unless defined in I(*_interfaces).
|
||||
This is a default setting for all switch interfaces.
|
||||
type: bool
|
||||
auto_exclude:
|
||||
description:
|
||||
- Each of the switch interfaces will be excluded from I(vlan_id)
|
||||
unless defined in I(*_interfaces).
|
||||
This is a default setting for all switch interfaces.
|
||||
type: bool
|
||||
aggregate:
|
||||
description: List of VLANs definitions.
|
||||
purge:
|
||||
description:
|
||||
- Purge VLANs not defined in the I(aggregate) parameter.
|
||||
default: no
|
||||
type: bool
|
||||
state:
|
||||
description:
|
||||
- action on the VLAN configuration.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Create vlan
|
||||
edgeswitch_vlan:
|
||||
vlan_id: 100
|
||||
name: voice
|
||||
action: present
|
||||
|
||||
- name: Add interfaces to VLAN
|
||||
edgeswitch_vlan:
|
||||
vlan_id: 100
|
||||
tagged_interfaces:
|
||||
- 0/1
|
||||
- 0/4-0/6
|
||||
|
||||
- name: setup three vlans and delete the rest
|
||||
edgeswitch_vlan:
|
||||
purge: true
|
||||
aggregate:
|
||||
- { vlan_id: 1, name: default, auto_untag: true, excluded_interfaces: 0/45-0/48 }
|
||||
- { vlan_id: 100, name: voice, auto_tag: true }
|
||||
- { vlan_id: 200, name: video, auto_exclude: true, untagged_interfaces: 0/45-0/48, tagged_interfaces: 0/49 }
|
||||
|
||||
- name: Delete vlan
|
||||
edgeswitch_vlan:
|
||||
vlan_id: 100
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration mode commands to send to the device
|
||||
returned: always
|
||||
type: list
|
||||
sample:
|
||||
- vlan database
|
||||
- vlan 100
|
||||
- vlan name 100 "test vlan"
|
||||
- exit
|
||||
- interface 0/1
|
||||
- vlan pvid 50
|
||||
- vlan participation include 50,100
|
||||
- vlan tagging 100
|
||||
- vlan participation exclude 200
|
||||
- no vlan tagging 200
|
||||
"""
|
||||
|
||||
import re
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.network.edgeswitch.edgeswitch import load_config, run_commands
|
||||
from ansible_collections.community.general.plugins.module_utils.network.edgeswitch.edgeswitch import build_aggregate_spec, map_params_to_obj
|
||||
from ansible_collections.community.general.plugins.module_utils.network.edgeswitch.edgeswitch_interface import InterfaceConfiguration, merge_interfaces
|
||||
|
||||
|
||||
def search_obj_in_list(vlan_id, lst):
|
||||
for o in lst:
|
||||
if o['vlan_id'] == vlan_id:
|
||||
return o
|
||||
|
||||
|
||||
def map_vlans_to_commands(want, have, module):
|
||||
commands = []
|
||||
vlans_added = []
|
||||
vlans_removed = []
|
||||
vlans_names = []
|
||||
|
||||
for w in want:
|
||||
vlan_id = w['vlan_id']
|
||||
name = w['name']
|
||||
state = w['state']
|
||||
|
||||
obj_in_have = search_obj_in_list(vlan_id, have)
|
||||
|
||||
if state == 'absent':
|
||||
if obj_in_have:
|
||||
vlans_removed.append(vlan_id)
|
||||
|
||||
elif state == 'present':
|
||||
if not obj_in_have:
|
||||
vlans_added.append(vlan_id)
|
||||
if name:
|
||||
vlans_names.append('vlan name {0} "{1}"'.format(vlan_id, name))
|
||||
else:
|
||||
if name:
|
||||
if name != obj_in_have['name']:
|
||||
vlans_names.append('vlan name {0} "{1}"'.format(vlan_id, name))
|
||||
|
||||
if module.params['purge']:
|
||||
for h in have:
|
||||
obj_in_want = search_obj_in_list(h['vlan_id'], want)
|
||||
# you can't delete vlan 1 on Edgeswitch
|
||||
if not obj_in_want and h['vlan_id'] != '1':
|
||||
vlans_removed.append(h['vlan_id'])
|
||||
|
||||
if vlans_removed:
|
||||
commands.append('no vlan {0}'.format(','.join(vlans_removed)))
|
||||
|
||||
if vlans_added:
|
||||
commands.append('vlan {0}'.format(','.join(vlans_added)))
|
||||
|
||||
if vlans_names:
|
||||
commands.extend(vlans_names)
|
||||
|
||||
if commands:
|
||||
commands.insert(0, 'vlan database')
|
||||
commands.append('exit')
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
class VlanInterfaceConfiguration(InterfaceConfiguration):
|
||||
""" class holding vlan definitions for a given interface
|
||||
"""
|
||||
def __init__(self):
|
||||
InterfaceConfiguration.__init__(self)
|
||||
self.tagged = []
|
||||
self.untagged = []
|
||||
self.excluded = []
|
||||
|
||||
def set_vlan(self, vlan_id, type):
|
||||
try:
|
||||
self.tagged.remove(vlan_id)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.untagged.remove(vlan_id)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.excluded.remove(vlan_id)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
f = getattr(self, type)
|
||||
f.append(vlan_id)
|
||||
|
||||
def gen_commands(self, port, module):
|
||||
""" to reduce commands generated by this module
|
||||
we group vlans changes to have a max of 5 vlan commands by interface
|
||||
"""
|
||||
exclude = []
|
||||
include = []
|
||||
tag = []
|
||||
untag = []
|
||||
pvid = []
|
||||
|
||||
for vlan_id in self.excluded:
|
||||
if vlan_id not in port['forbidden_vlans']:
|
||||
exclude.append(vlan_id)
|
||||
|
||||
if vlan_id in port['tagged_vlans']:
|
||||
untag.append(vlan_id)
|
||||
|
||||
for vlan_id in self.untagged:
|
||||
if vlan_id in port['forbidden_vlans'] or vlan_id not in port['untagged_vlans'] and vlan_id not in port['tagged_vlans']:
|
||||
include.append(vlan_id)
|
||||
|
||||
if vlan_id in port['tagged_vlans']:
|
||||
untag.append(vlan_id)
|
||||
|
||||
if vlan_id != port['pvid_mode']:
|
||||
pvid.append(vlan_id)
|
||||
|
||||
for vlan_id in self.tagged:
|
||||
if vlan_id not in port['tagged_vlans']:
|
||||
tag.append(vlan_id)
|
||||
include.append(vlan_id)
|
||||
|
||||
if include:
|
||||
self.commands.append('vlan participation include {0}'.format(','.join(include)))
|
||||
|
||||
if pvid:
|
||||
if len(pvid) > 1:
|
||||
module.fail_json(msg='{0} can\'t have more than one untagged vlan')
|
||||
return
|
||||
self.commands.append('vlan pvid {0}'.format(pvid[0]))
|
||||
|
||||
if untag:
|
||||
self.commands.append('no vlan tagging {0}'.format(','.join(untag)))
|
||||
|
||||
if tag:
|
||||
self.commands.append('vlan tagging {0}'.format(','.join(tag)))
|
||||
|
||||
if exclude:
|
||||
self.commands.append('vlan participation exclude {0}'.format(','.join(exclude)))
|
||||
|
||||
|
||||
def set_interfaces_vlan(interfaces_param, interfaces, vlan_id, type):
|
||||
""" set vlan_id type for each interface in interfaces_param on interfaces
|
||||
unrange interfaces_param if needed
|
||||
"""
|
||||
if interfaces_param:
|
||||
for i in interfaces_param:
|
||||
match = re.search(r'(\d+)\/(\d+)-(\d+)\/(\d+)', i)
|
||||
if match:
|
||||
group = match.group(1)
|
||||
start = int(match.group(2))
|
||||
end = int(match.group(4))
|
||||
for x in range(start, end + 1):
|
||||
key = '{0}/{1}'.format(group, x)
|
||||
interfaces[key].set_vlan(vlan_id, type)
|
||||
else:
|
||||
interfaces[i].set_vlan(vlan_id, type)
|
||||
|
||||
|
||||
def map_interfaces_to_commands(want, ports, module):
|
||||
commands = list()
|
||||
|
||||
# generate a configuration for each interface
|
||||
interfaces = {}
|
||||
for key, value in ports.items():
|
||||
interfaces[key] = VlanInterfaceConfiguration()
|
||||
|
||||
for w in want:
|
||||
state = w['state']
|
||||
if state != 'present':
|
||||
continue
|
||||
|
||||
auto_tag = w['auto_tag']
|
||||
auto_untag = w['auto_untag']
|
||||
auto_exclude = w['auto_exclude']
|
||||
vlan_id = w['vlan_id']
|
||||
tagged_interfaces = w['tagged_interfaces']
|
||||
untagged_interfaces = w['untagged_interfaces']
|
||||
excluded_interfaces = w['excluded_interfaces']
|
||||
|
||||
# set the default type, if any
|
||||
for key, value in ports.items():
|
||||
if auto_tag:
|
||||
interfaces[key].tagged.append(vlan_id)
|
||||
elif auto_exclude:
|
||||
interfaces[key].excluded.append(vlan_id)
|
||||
elif auto_untag:
|
||||
interfaces[key].untagged.append(vlan_id)
|
||||
|
||||
# set explicit definitions
|
||||
set_interfaces_vlan(tagged_interfaces, interfaces, vlan_id, 'tagged')
|
||||
set_interfaces_vlan(untagged_interfaces, interfaces, vlan_id, 'untagged')
|
||||
set_interfaces_vlan(excluded_interfaces, interfaces, vlan_id, 'excluded')
|
||||
|
||||
# generate commands for each interface
|
||||
for i, interface in interfaces.items():
|
||||
port = ports[i]
|
||||
interface.gen_commands(port, module)
|
||||
|
||||
# reduce them using range syntax when possible
|
||||
interfaces = merge_interfaces(interfaces)
|
||||
|
||||
# final output
|
||||
for i, interface in interfaces.items():
|
||||
if len(interface.commands) > 0:
|
||||
commands.append('interface {0}'.format(i))
|
||||
commands.extend(interface.commands)
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
def parse_vlan_brief(vlan_out):
|
||||
have = []
|
||||
for line in vlan_out.split('\n'):
|
||||
obj = re.match(r'(?P<vlan_id>\d+)\s+(?P<name>[^\s]+)\s+', line)
|
||||
if obj:
|
||||
have.append(obj.groupdict())
|
||||
return have
|
||||
|
||||
|
||||
def unrange(vlans):
|
||||
res = []
|
||||
for vlan in vlans:
|
||||
match = re.match(r'(\d+)-(\d+)', vlan)
|
||||
if match:
|
||||
start = int(match.group(1))
|
||||
end = int(match.group(2))
|
||||
for vlan_id in range(start, end + 1):
|
||||
res.append(str(vlan_id))
|
||||
else:
|
||||
res.append(vlan)
|
||||
return res
|
||||
|
||||
|
||||
def parse_interfaces_switchport(cmd_out):
|
||||
ports = dict()
|
||||
objs = re.findall(
|
||||
r'Port: (\d+\/\d+)\n'
|
||||
'VLAN Membership Mode:(.*)\n'
|
||||
'Access Mode VLAN:(.*)\n'
|
||||
'General Mode PVID:(.*)\n'
|
||||
'General Mode Ingress Filtering:(.*)\n'
|
||||
'General Mode Acceptable Frame Type:(.*)\n'
|
||||
'General Mode Dynamically Added VLANs:(.*)\n'
|
||||
'General Mode Untagged VLANs:(.*)\n'
|
||||
'General Mode Tagged VLANs:(.*)\n'
|
||||
'General Mode Forbidden VLANs:(.*)\n', cmd_out)
|
||||
for o in objs:
|
||||
port = {
|
||||
'interface': o[0],
|
||||
'pvid_mode': o[3].replace("(default)", "").strip(),
|
||||
'untagged_vlans': unrange(o[7].strip().split(',')),
|
||||
'tagged_vlans': unrange(o[8].strip().split(',')),
|
||||
'forbidden_vlans': unrange(o[9].strip().split(','))
|
||||
}
|
||||
ports[port['interface']] = port
|
||||
return ports
|
||||
|
||||
|
||||
def map_ports_to_obj(module):
|
||||
return parse_interfaces_switchport(run_commands(module, ['show interfaces switchport'])[0])
|
||||
|
||||
|
||||
def map_config_to_obj(module):
|
||||
return parse_vlan_brief(run_commands(module, ['show vlan brief'])[0])
|
||||
|
||||
|
||||
def check_params(module, want):
|
||||
""" Deeper checks on parameters
|
||||
"""
|
||||
def check_parmams_interface(interfaces):
|
||||
if interfaces:
|
||||
for i in interfaces:
|
||||
match = re.search(r'(\d+)\/(\d+)-(\d+)\/(\d+)', i)
|
||||
if match:
|
||||
if match.group(1) != match.group(3):
|
||||
module.fail_json(msg="interface range must be within same group: " + i)
|
||||
else:
|
||||
match = re.search(r'(\d+)\/(\d+)', i)
|
||||
if not match:
|
||||
module.fail_json(msg="wrong interface format: " + i)
|
||||
|
||||
for w in want:
|
||||
auto_tag = w['auto_tag']
|
||||
auto_untag = w['auto_untag']
|
||||
auto_exclude = w['auto_exclude']
|
||||
|
||||
c = 0
|
||||
if auto_tag:
|
||||
c = c + 1
|
||||
|
||||
if auto_untag:
|
||||
c = c + 1
|
||||
|
||||
if auto_exclude:
|
||||
c = c + 1
|
||||
|
||||
if c > 1:
|
||||
module.fail_json(msg="parameters are mutually exclusive: auto_tag, auto_untag, auto_exclude")
|
||||
return
|
||||
|
||||
check_parmams_interface(w['tagged_interfaces'])
|
||||
check_parmams_interface(w['untagged_interfaces'])
|
||||
check_parmams_interface(w['excluded_interfaces'])
|
||||
w['vlan_id'] = str(w['vlan_id'])
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
element_spec = dict(
|
||||
vlan_id=dict(type='int'),
|
||||
name=dict(),
|
||||
tagged_interfaces=dict(type='list'),
|
||||
untagged_interfaces=dict(type='list'),
|
||||
excluded_interfaces=dict(type='list'),
|
||||
auto_tag=dict(type='bool'),
|
||||
auto_exclude=dict(type='bool'),
|
||||
auto_untag=dict(type='bool'),
|
||||
state=dict(default='present',
|
||||
choices=['present', 'absent'])
|
||||
)
|
||||
|
||||
argument_spec = build_aggregate_spec(
|
||||
element_spec,
|
||||
['vlan_id'],
|
||||
dict(purge=dict(default=False, type='bool'))
|
||||
)
|
||||
|
||||
required_one_of = [['vlan_id', 'aggregate']]
|
||||
mutually_exclusive = [
|
||||
['vlan_id', 'aggregate'],
|
||||
['auto_tag', 'auto_untag', 'auto_exclude']]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
result = {'changed': False}
|
||||
|
||||
want = map_params_to_obj(module)
|
||||
have = map_config_to_obj(module)
|
||||
|
||||
check_params(module, want)
|
||||
|
||||
# vlans are not created/deleted in configure mode
|
||||
commands = map_vlans_to_commands(want, have, module)
|
||||
result['commands'] = commands
|
||||
|
||||
if commands:
|
||||
if not module.check_mode:
|
||||
run_commands(module, commands, check_rc=False)
|
||||
result['changed'] = True
|
||||
|
||||
ports = map_ports_to_obj(module)
|
||||
|
||||
# interfaces vlan are set in configure mode
|
||||
commands = map_interfaces_to_commands(want, ports, module)
|
||||
result['commands'].extend(commands)
|
||||
|
||||
if commands:
|
||||
if not module.check_mode:
|
||||
load_config(module, commands)
|
||||
result['changed'] = True
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue