diff options
Diffstat (limited to 'lib')
13 files changed, 881 insertions, 15 deletions
diff --git a/lib/ansible/module_utils/network/common/utils.py b/lib/ansible/module_utils/network/common/utils.py index 074821daac..afe419aa89 100644 --- a/lib/ansible/module_utils/network/common/utils.py +++ b/lib/ansible/module_utils/network/common/utils.py @@ -594,6 +594,13 @@ def validate_config(spec, data): return validated_data +def search_obj_in_list(name, lst, key='name'): + for item in lst: + if item[key] == name: + return item + return None + + class Template: def __init__(self): diff --git a/lib/ansible/module_utils/network/ios/argspec/facts/facts.py b/lib/ansible/module_utils/network/ios/argspec/facts/facts.py index 6d09ec4892..00bc062b0f 100644 --- a/lib/ansible/module_utils/network/ios/argspec/facts/facts.py +++ b/lib/ansible/module_utils/network/ios/argspec/facts/facts.py @@ -22,7 +22,9 @@ class FactsArgs(object): 'all', '!all', 'interfaces', - '!interfaces' + '!interfaces', + 'l2_interfaces', + '!l2_interfaces' ] argument_spec = { diff --git a/lib/ansible/module_utils/network/ios/argspec/l2_interfaces/__init__.py b/lib/ansible/module_utils/network/ios/argspec/l2_interfaces/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/ansible/module_utils/network/ios/argspec/l2_interfaces/__init__.py diff --git a/lib/ansible/module_utils/network/ios/argspec/l2_interfaces/l2_interfaces.py b/lib/ansible/module_utils/network/ios/argspec/l2_interfaces/l2_interfaces.py new file mode 100644 index 0000000000..6d7e406107 --- /dev/null +++ b/lib/ansible/module_utils/network/ios/argspec/l2_interfaces/l2_interfaces.py @@ -0,0 +1,53 @@ +# +# -*- 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_l2_interfaces module +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class L2_InterfacesArgs(object): + + def __init__(self, **kwargs): + pass + + argument_spec = {'config': {'elements': 'dict', + 'options': {'name': {'type': 'str', 'required': True}, + 'access': {'type': 'dict', + 'options': {'vlan': {'type': 'int'}} + }, + 'trunk': {'type': 'dict', + 'options': {'allowed_vlans': {'type': 'list'}, + 'encapsulation': {'type': 'str', + 'choices': + ['dot1q', 'isl', 'negotiate']}, + 'native_vlan': {'type': 'int'}, + 'pruning_vlans': {'type': 'list'}} + }}, + 'type': 'list'}, + 'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'], + 'default': 'merged', + 'type': 'str'}} diff --git a/lib/ansible/module_utils/network/ios/config/l2_interfaces/__init__.py b/lib/ansible/module_utils/network/ios/config/l2_interfaces/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/ansible/module_utils/network/ios/config/l2_interfaces/__init__.py diff --git a/lib/ansible/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py b/lib/ansible/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py new file mode 100644 index 0000000000..703cf399fb --- /dev/null +++ b/lib/ansible/module_utils/network/ios/config/l2_interfaces/l2_interfaces.py @@ -0,0 +1,312 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The ios_l2_interfaces 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 +from ansible.module_utils.network.ios.utils.utils import remove_command_from_config_list, add_command_to_config_list +from ansible.module_utils.network.ios.utils.utils import filter_dict_having_none_value, remove_duplicate_interface + + +class L2_Interfaces(ConfigBase): + """ + The ios_l2_interfaces class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'l2_interfaces', + ] + + access_cmds = {'access_vlan': 'switchport access vlan'} + trunk_cmds = {'encapsulation': 'switchport trunk encapsulation', 'pruning_vlans': 'switchport trunk pruning vlan', + 'native_vlan': 'switchport trunk native vlan', 'allowed_vlans': 'switchport trunk allowed vlan'} + + 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('l2_interfaces') + if not interfaces_facts: + return [] + + return interfaces_facts + + def execute_module(self): + """ Execute the module + :rtype: A dictionary + :returns: The result from moduel execution + """ + result = {'changed': False} + commands = [] + warnings = [] + existing_facts = self.get_interfaces_facts() + commands.extend(self.set_config(existing_facts)) + result['before'] = existing_facts + if commands: + if not self._module.check_mode: + self._connection.edit_config(commands) + result['changed'] = True + result['commands'] = commands + + interfaces_facts = self.get_interfaces_facts() + + if result['changed']: + result['after'] = interfaces_facts + result['warnings'] = warnings + return result + + def set_config(self, existing_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 deisred configuration + """ + + want = self._module.params['config'] + have = existing_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 deisred configuration + """ + commands = [] + + state = self._module.params['state'] + if state == 'overridden': + commands = self._state_overridden(want, have, self._module) + elif state == 'deleted': + commands = self._state_deleted(want, have) + elif state == 'merged': + commands = self._state_merged(want, have, self._module) + elif state == 'replaced': + commands = self._state_replaced(want, have, self._module) + + return commands + + def _state_replaced(self, want, have, module): + """ The command generator when state is replaced + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :param interface_type: interface type + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the deisred configuration + """ + commands = [] + + for interface in want: + for each in have: + if each['name'] == interface['name']: + break + else: + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each, module)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_overridden(self, want, have, module): + """ The command generator when state is overridden + :param want: the desired configuration as a dictionary + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + + for each in have: + for interface in want: + if each['name'] == interface['name']: + break + else: + # We didn't find a matching desired state, which means we can + # pretend we recieved an empty desired state. + interface = dict(name=each['name']) + kwargs = {'want': interface, 'have': each} + commands.extend(self._clear_config(**kwargs)) + continue + have_dict = filter_dict_having_none_value(interface, each) + commands.extend(self._clear_config(dict(), have_dict)) + commands.extend(self._set_config(interface, each, module)) + # Remove the duplicate interface call + commands = remove_duplicate_interface(commands) + + return commands + + def _state_merged(self, want, have, module): + """ The command generator when state is merged + :param want: the additive configuration as a dictionary + :param obj_in_have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + + for interface in want: + for each in have: + if each['name'] == interface['name']: + break + else: + continue + commands.extend(self._set_config(interface, each, module)) + + return commands + + def _state_deleted(self, want, have): + """ The command generator when state is deleted + :param want: the objects from which the configuration should be removed + :param obj_in_have: the current configuration as a dictionary + :param interface_type: interface type + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + + if want: + for interface in want: + for each in have: + if each['name'] == interface['name']: + break + else: + continue + interface = dict(name=interface['name']) + commands.extend(self._clear_config(interface, each)) + else: + for each in have: + want = dict() + commands.extend(self._clear_config(want, each)) + + return commands + + def _check_for_correct_vlan_range(self, vlan, module): + # Function to check if the VLAN range passed is Valid + for each in vlan: + vlan_range = each.split('-') + if len(vlan_range) > 1: + if vlan_range[0] < vlan_range[1]: + return True + else: + module.fail_json(msg='Command rejected: Bad VLAN list - end of range not larger than the' + ' start of range!') + else: + return True + + def _set_config(self, want, have, module): + # Set the interface config based on the want and have config + commands = [] + interface = 'interface ' + want['name'] + + # Get the diff b/w want and have + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) + want_trunk = dict(want_dict).get('trunk') + have_trunk = dict(have_dict).get('trunk') + if want_trunk and have_trunk: + diff = set(tuple(dict(want_dict).get('trunk'))) - set(tuple(dict(have_dict).get('trunk'))) + else: + diff = want_dict - have_dict + + if diff: + diff = dict(diff) + + if diff.get('access'): + cmd = 'switchport access vlan {0}'.format(diff.get('access')[0][1]) + add_command_to_config_list(interface, cmd, commands) + + if want_trunk: + if diff.get('trunk'): + diff = dict(diff.get('trunk')) + if diff.get('encapsulation'): + cmd = self.trunk_cmds['encapsulation'] + ' {0}'.format(diff.get('encapsulation')) + add_command_to_config_list(interface, cmd, commands) + if diff.get('native_vlan'): + cmd = self.trunk_cmds['native_vlan'] + ' {0}'.format(diff.get('native_vlan')) + add_command_to_config_list(interface, cmd, commands) + allowed_vlans = diff.get('allowed_vlans') + pruning_vlans = diff.get('pruning_vlans') + + if allowed_vlans and self._check_for_correct_vlan_range(allowed_vlans, module): + allowed_vlans = ','.join(allowed_vlans) + cmd = self.trunk_cmds['allowed_vlans'] + ' {0}'.format(allowed_vlans) + add_command_to_config_list(interface, cmd, commands) + if pruning_vlans and self._check_for_correct_vlan_range(pruning_vlans, module): + pruning_vlans = ','.join(pruning_vlans) + cmd = self.trunk_cmds['pruning_vlans'] + ' {0}'.format(pruning_vlans) + add_command_to_config_list(interface, cmd, commands) + + return commands + + def _clear_config(self, want, have): + # Delete the interface config based on the want and have config + commands = [] + if want.get('name'): + interface = 'interface ' + want['name'] + else: + interface = 'interface ' + have['name'] + + 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'): + if have.get('access').get('vlan') != want.get('access').get('vlan'): + remove_command_from_config_list(interface, L2_Interfaces.access_cmds['access_vlan'], commands) + + if have.get('trunk') and want.get('trunk') is None: + # Check when no config is passed + if have.get('trunk').get('encapsulation'): + remove_command_from_config_list(interface, self.trunk_cmds['encapsulation'], commands) + if have.get('trunk').get('native_vlan'): + remove_command_from_config_list(interface, self.trunk_cmds['native_vlan'], commands) + if have.get('trunk').get('allowed_vlans'): + remove_command_from_config_list(interface, self.trunk_cmds['allowed_vlans'], commands) + if have.get('trunk').get('pruning_vlans'): + remove_command_from_config_list(interface, self.trunk_cmds['pruning_vlans'], commands) + elif have.get('trunk') and want.get('trunk'): + # Check when config is passed, also used in replaced and override state + if have.get('trunk').get('encapsulation')\ + and have.get('trunk').get('encapsulation') != want.get('trunk').get('encapsulation'): + remove_command_from_config_list(interface, self.trunk_cmds['encapsulation'], commands) + if have.get('trunk').get('native_vlan') \ + and have.get('trunk').get('native_vlan') != want.get('trunk').get('native_vlan'): + remove_command_from_config_list(interface, self.trunk_cmds['native_vlan'], commands) + if have.get('trunk').get('allowed_vlans') \ + and have.get('trunk').get('allowed_vlans') != want.get('trunk').get('allowed_vlans'): + remove_command_from_config_list(interface, self.trunk_cmds['allowed_vlans'], commands) + if have.get('trunk').get('pruning_vlans') \ + and have.get('trunk').get('pruning_vlans') != want.get('trunk').get('pruning_vlans'): + remove_command_from_config_list(interface, self.trunk_cmds['pruning_vlans'], commands) + + return commands diff --git a/lib/ansible/module_utils/network/ios/facts/facts.py b/lib/ansible/module_utils/network/ios/facts/facts.py index e321d54419..a8a9eae793 100644 --- a/lib/ansible/module_utils/network/ios/facts/facts.py +++ b/lib/ansible/module_utils/network/ios/facts/facts.py @@ -16,6 +16,7 @@ __metaclass__ = type 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.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.legacy.base import Default, Hardware, Interfaces, Config @@ -28,6 +29,7 @@ FACT_LEGACY_SUBSETS = dict( FACT_RESOURCE_SUBSETS = dict( interfaces=InterfacesFacts, + l2_interfaces=L2_InterfacesFacts, ) diff --git a/lib/ansible/module_utils/network/ios/facts/l2_interfaces/__init__.py b/lib/ansible/module_utils/network/ios/facts/l2_interfaces/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/ansible/module_utils/network/ios/facts/l2_interfaces/__init__.py diff --git a/lib/ansible/module_utils/network/ios/facts/l2_interfaces/l2_interfaces.py b/lib/ansible/module_utils/network/ios/facts/l2_interfaces/l2_interfaces.py new file mode 100644 index 0000000000..d175f31386 --- /dev/null +++ b/lib/ansible/module_utils/network/ios/facts/l2_interfaces/l2_interfaces.py @@ -0,0 +1,108 @@ +# +# -*- 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 interfaces 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 +import re +from ansible.module_utils.network.common import utils +from ansible.module_utils.network.ios.utils.utils import get_interface_type, normalize_interface +from ansible.module_utils.network.ios.argspec.l2_interfaces.l2_interfaces import L2_InterfacesArgs + + +class L2_InterfacesFacts(object): + """ The ios l2 interfaces fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = L2_InterfacesArgs.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 interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + objs = [] + + if not data: + data = connection.get('show running-config | section ^interface') + # operate on a collection of resource x + config = data.split('interface ') + for conf in config: + if conf: + obj = self.render_config(self.generated_spec, conf) + if obj: + objs.append(obj) + + facts = {} + if objs: + facts['l2_interfaces'] = [] + params = utils.validate_config(self.argument_spec, {'config': objs}) + for cfg in params['config']: + facts['l2_interfaces'].append(utils.remove_empties(cfg)) + ansible_facts['ansible_network_resources'].update(facts) + + return ansible_facts + + def render_config(self, spec, conf): + """ + 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) + match = re.search(r'^(\S+)', conf) + intf = match.group(1) + + if get_interface_type(intf) == 'unknown': + return {} + + if intf.lower().startswith('gi'): + # populate the facts from the configuration + config['name'] = normalize_interface(intf) + + has_access = utils.parse_conf_arg(conf, 'switchport access vlan') + if has_access: + config["access"] = {"vlan": int(has_access)} + + trunk = dict() + trunk["encapsulation"] = utils.parse_conf_arg(conf, 'encapsulation') + native_vlan = utils.parse_conf_arg(conf, 'native vlan') + if native_vlan: + trunk["native_vlan"] = int(native_vlan) + allowed_vlan = utils.parse_conf_arg(conf, 'allowed vlan') + if allowed_vlan: + trunk["allowed_vlans"] = allowed_vlan.split(',') + pruning_vlan = utils.parse_conf_arg(conf, 'pruning vlan') + if pruning_vlan: + trunk['pruning_vlans'] = pruning_vlan.split(',') + + config['trunk'] = trunk + + return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/ios/utils/utils.py b/lib/ansible/module_utils/network/ios/utils/utils.py index 3b1272c456..16a05602e6 100644 --- a/lib/ansible/module_utils/network/ios/utils/utils.py +++ b/lib/ansible/module_utils/network/ios/utils/utils.py @@ -32,6 +32,24 @@ def dict_to_set(sample_dict): test_dict = {} for k, v in iteritems(sample_dict): if v is not None: + if isinstance(v, list): + if isinstance(v[0], dict): + li = [] + for each in v: + for key, value in iteritems(each): + if isinstance(value, list): + each[key] = tuple(value) + li.extend(tuple(each.items())) + v = tuple(li) + else: + v = tuple(v) + elif isinstance(v, dict): + li = [] + for key, value in iteritems(v): + if isinstance(value, list): + v[key] = tuple(value) + li.extend(tuple(v.items())) + v = tuple(li) test_dict.update({k: v}) return_set = set(tuple(test_dict.items())) return return_set @@ -40,8 +58,15 @@ def dict_to_set(sample_dict): def filter_dict_having_none_value(want, have): # Generate dict with have dict value which is None in want dict test_dict = dict() + test_key_dict = dict() test_dict['name'] = want.get('name') for k, v in iteritems(want): + if isinstance(v, dict): + for key, value in iteritems(v): + if value is None: + dict_val = have.get(k).get(key) + test_key_dict.update({key: dict_val}) + test_dict.update({k: test_key_dict}) if v is None: val = have.get(k) test_dict.update({k: val}) @@ -61,13 +86,6 @@ def remove_duplicate_interface(commands): return set_cmd -def search_obj_in_list(name, lst): - for o in lst: - if o['name'] == name: - return o - return None - - def normalize_interface(name): """Return the normalized interface name """ diff --git a/lib/ansible/modules/network/ios/ios_l2_interface.py b/lib/ansible/modules/network/ios/_ios_l2_interface.py index 1de0b374bb..bcfcb880ea 100644 --- a/lib/ansible/modules/network/ios/ios_l2_interface.py +++ b/lib/ansible/modules/network/ios/_ios_l2_interface.py @@ -9,7 +9,7 @@ __metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], + 'status': ['deprecated'], 'supported_by': 'network'} DOCUMENTATION = """ @@ -21,6 +21,10 @@ short_description: Manage Layer-2 interface on Cisco IOS devices. description: - This module provides declarative management of Layer-2 interfaces on Cisco IOS devices. +deprecated: + removed_in: '2.13' + alternative: ios_l2_interfaces + why: Newer and updated modules released with more functionality in Ansible 2.9 author: - Nathaniel Case (@Qalthos) options: @@ -68,27 +72,23 @@ EXAMPLES = """ ios_l2_interface: name: GigabitEthernet0/5 state: unconfigured - - name: Ensure GigabitEthernet0/5 is configured for access vlan 20 ios_l2_interface: name: GigabitEthernet0/5 mode: access access_vlan: 20 - - name: Ensure GigabitEthernet0/5 only has vlans 5-10 as trunk vlans ios_l2_interface: name: GigabitEthernet0/5 mode: trunk native_vlan: 10 trunk_vlans: 5-10 - - name: Ensure GigabitEthernet0/5 is a trunk port and ensure 2-50 are being tagged (doesn't mean others aren't also being tagged) ios_l2_interface: name: GigabitEthernet0/5 mode: trunk native_vlan: 10 trunk_vlans: 2-50 - - name: Ensure these VLANs are not being tagged on the trunk ios_l2_interface: name: GigabitEthernet0/5 @@ -112,7 +112,7 @@ from copy import deepcopy from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.network.common.utils import remove_default_spec -from ansible.module_utils.network.ios.ios import load_config, run_commands +from ansible.module_utils.network.ios.ios import get_config, load_config, run_commands from ansible.module_utils.network.ios.ios import ios_argument_spec diff --git a/lib/ansible/modules/network/ios/ios_facts.py b/lib/ansible/modules/network/ios/ios_facts.py index e4a66b9a0b..7bf72fdef2 100644 --- a/lib/ansible/modules/network/ios/ios_facts.py +++ b/lib/ansible/modules/network/ios/ios_facts.py @@ -54,7 +54,7 @@ options: to a given subset. Possible values for this argument include all and the resources like interfaces, vlans etc. Can specify a list of values to include a larger subset. - choices: ['all', '!all', 'interfaces', '!interfaces'] + choices: ['all', '!all', 'interfaces', '!interfaces', 'l2_interfaces', '!l2_interfaces'] version_added: "2.9" """ @@ -90,6 +90,11 @@ EXAMPLES = """ ios_facts: gather_subset: min gather_network_resources: interfaces + +- name: Gather L2 interfaces resource and minimal legacy facts + ios_facts: + gather_subset: min + gather_network_resources: l2_interfaces """ RETURN = """ diff --git a/lib/ansible/modules/network/ios/ios_l2_interfaces.py b/lib/ansible/modules/network/ios/ios_l2_interfaces.py new file mode 100644 index 0000000000..8c9f6b838d --- /dev/null +++ b/lib/ansible/modules/network/ios/ios_l2_interfaces.py @@ -0,0 +1,359 @@ +#!/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_l2_interfaces +""" + + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'network'} + +DOCUMENTATION = """ +--- +module: ios_l2_interfaces +version_added: 2.9 +short_description: Manage Layer-2 interface on Cisco IOS devices. +description: This module provides declarative management of Layer-2 interface on Cisco IOS 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 Layer-2 interface options + type: list + elements: dict + suboptions: + name: + description: + - Full name of the interface excluding any logical unit number, i.e. GigabitEthernet0/1. + type: str + required: True + access: + description: + - Switchport mode access command to configure the interface as a layer 2 access. + type: dict + suboptions: + vlan: + description: + - Configure given VLAN in access port. It's used as the access VLAN ID. + type: int + trunk: + description: + - Switchport mode trunk command to configure the interface as a Layer 2 trunk. + Note The encapsulation is always set to dot1q. + type: dict + suboptions: + allowed_vlans: + description: + - List of allowed VLANs in a given trunk port. These are the only VLANs that will be + configured on the trunk. + type: list + native_vlan: + description: + - Native VLAN to be configured in trunk port. It's used as the trunk native VLAN ID. + type: int + encapsulation: + description: + - Trunking encapsulation when interface is in trunking mode. + choices: ['dot1q','isl','negotiate'] + type: str + pruning_vlans: + description: + - Pruning VLAN to be configured in trunk port. It's used as the trunk pruning VLAN ID. + type: list + state: + choices: + - merged + - replaced + - overridden + - deleted + default: merged + description: + - The state the configuration should be left in + type: str +""" + +EXAMPLES = """ +--- + +# Using merged + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# media-type rj45 +# negotiation auto + +- name: Merge provided configuration with device configuration + ios_l2_interfaces: + config: + - name: GigabitEthernet0/1 + access: + vlan: 10 + - name: GigabitEthernet0/2 + trunk: + allowed_vlan: 10-20, 40 + native_vlan: 20 + pruning_vlan: 10,20 + encapsulation: dot1q + state: merged + +# After state: +# ------------ +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 10 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport trunk allowed vlan 10-20,40 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 20 +# switchport trunk pruning vlan 10,20 +# media-type rj45 +# negotiation auto + +# Using replaced + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# media-type rj45 +# negotiation auto + +- name: Replaces device configuration of listed l2 interfaces with provided configuration + ios_l2_interfaces: + config: + - name: GigabitEthernet0/2 + trunk: + - allowed_vlan: 20-25,40 + native_vlan: 20 + pruning_vlan: 10 + encapsulation: isl + state: replaced + +# After state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport trunk allowed vlan 20-25,40 +# switchport trunk encapsulation isl +# switchport trunk native vlan 20 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + +# Using overridden + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 20 +# media-type rj45 +# negotiation auto + +- name: Override device configuration of all l2 interfaces with provided configuration + ios_l2_interfaces: + config: + - name: GigabitEthernet0/2 + access: + vlan: 20 + state: overridden + +# After state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# media-type rj45 +# negotiation auto + +# Using Deleted + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk allowed vlan 20-40,60,80 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + +- name: Delete IOS L2 interfaces as in given arguments + ios_l2_interfaces: + config: + - name: GigabitEthernet0/1 + state: deleted + +# After state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk allowed vlan 20-40,60,80 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + + +# Using Deleted without any config passed +#"(NOTE: This will delete all of configured resource module attributes from each configured interface)" + +# Before state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# switchport access vlan 20 +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# switchport access vlan 20 +# switchport trunk allowed vlan 20-40,60,80 +# switchport trunk encapsulation dot1q +# switchport trunk native vlan 10 +# switchport trunk pruning vlan 10 +# media-type rj45 +# negotiation auto + +- name: Delete IOS L2 interfaces as in given arguments + ios_l2_interfaces: + state: deleted + +# After state: +# ------------- +# +# viosl2#show running-config | section ^interface +# interface GigabitEthernet0/1 +# description Configured by Ansible +# negotiation auto +# interface GigabitEthernet0/2 +# description This is test +# media-type rj45 +# negotiation auto + +""" + +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 paramters 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 paramters above. +commands: + description: The set of commands pushed to the remote device + returned: always + type: list + sample: ['interface GigabitEthernet0/1', 'switchport access vlan 20'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.network.ios.argspec.l2_interfaces.l2_interfaces import L2_InterfacesArgs +from ansible.module_utils.network.ios.config.l2_interfaces.l2_interfaces import L2_Interfaces + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=L2_InterfacesArgs.argument_spec, + supports_check_mode=True) + + result = L2_Interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() |