From 1c83202f552d10430f70849d927e4961deedf140 Mon Sep 17 00:00:00 2001 From: Sumit Jaiswal Date: Mon, 8 Jul 2019 01:14:52 +0530 Subject: [PATCH 01/11] ios vlans Signed-off-by: Sumit Jaiswal --- library/__init__.py | 0 library/ios_facts.py | 108 ++++++++++ library/ios_vlans.py | 199 ++++++++++++++++++ module_utils/__init__.py | 0 module_utils/__init__.pyc | Bin 0 -> 111 bytes module_utils/network/__init__.py | 0 module_utils/network/__init__.pyc | Bin 0 -> 119 bytes module_utils/network/ios/__init__.py | 0 module_utils/network/ios/__init__.pyc | Bin 0 -> 123 bytes module_utils/network/ios/argspec/__init__.py | 0 .../network/ios/argspec/facts/__init__.py | 0 .../network/ios/argspec/facts/facts.py | 28 +++ .../network/ios/argspec/vlans/__init__.py | 0 .../network/ios/argspec/vlans/vlans.py | 46 ++++ module_utils/network/ios/config/__init__.py | 0 module_utils/network/ios/config/__init__.pyc | Bin 0 -> 130 bytes .../network/ios/config/vlans/__init__.py | 0 .../network/ios/config/vlans/__init__.pyc | Bin 0 -> 136 bytes .../network/ios/config/vlans/vlans.py | 185 ++++++++++++++++ .../network/ios/config/vlans/vlans.pyc | Bin 0 -> 6401 bytes module_utils/network/ios/facts/__init__.py | 0 module_utils/network/ios/facts/facts.py | 49 +++++ .../network/ios/facts/vlans/__init__.py | 0 module_utils/network/ios/facts/vlans/vlans.py | 113 ++++++++++ module_utils/network/ios/utils/__init__.py | 0 module_utils/network/ios/utils/utils.py | 100 +++++++++ 26 files changed, 828 insertions(+) create mode 100644 library/__init__.py create mode 100644 library/ios_facts.py create mode 100644 library/ios_vlans.py create mode 100644 module_utils/__init__.py create mode 100644 module_utils/__init__.pyc create mode 100644 module_utils/network/__init__.py create mode 100644 module_utils/network/__init__.pyc create mode 100644 module_utils/network/ios/__init__.py create mode 100644 module_utils/network/ios/__init__.pyc create mode 100644 module_utils/network/ios/argspec/__init__.py create mode 100644 module_utils/network/ios/argspec/facts/__init__.py create mode 100644 module_utils/network/ios/argspec/facts/facts.py create mode 100644 module_utils/network/ios/argspec/vlans/__init__.py create mode 100644 module_utils/network/ios/argspec/vlans/vlans.py create mode 100644 module_utils/network/ios/config/__init__.py create mode 100644 module_utils/network/ios/config/__init__.pyc create mode 100644 module_utils/network/ios/config/vlans/__init__.py create mode 100644 module_utils/network/ios/config/vlans/__init__.pyc create mode 100644 module_utils/network/ios/config/vlans/vlans.py create mode 100644 module_utils/network/ios/config/vlans/vlans.pyc create mode 100644 module_utils/network/ios/facts/__init__.py create mode 100644 module_utils/network/ios/facts/facts.py create mode 100644 module_utils/network/ios/facts/vlans/__init__.py create mode 100644 module_utils/network/ios/facts/vlans/vlans.py create mode 100644 module_utils/network/ios/utils/__init__.py create mode 100644 module_utils/network/ios/utils/utils.py diff --git a/library/__init__.py b/library/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/library/ios_facts.py b/library/ios_facts.py new file mode 100644 index 0000000..ee3a73c --- /dev/null +++ b/library/ios_facts.py @@ -0,0 +1,108 @@ +#!/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) +""" +The module file for myos_facts +""" + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': [u'preview'], + 'supported_by': 'network'} + + +DOCUMENTATION = """ +--- +module: ios_facts +version_added: 2.9 +short_description: Get facts about Cisco ios devices. +description: + - Collects facts from network devices running the ios operating + system. This module places the facts gathered in the fact tree keyed by the + respective resource name. The facts module will always collect a + base set of facts from the device and can enable or disable + collection of additional facts. +author: [u'Sumit Jaiswal (@justjais)'] +notes: + - Tested against Cisco IOSv Version 15.2 on VIRL +options: + gather_subset: + description: + - When supplied, this argument will restrict the facts collected + to a given subset. Possible values for this argument include + all, min, hardware, config, legacy, and lacp_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: 'all' + version_added: "2.2" + gather_network_resources: + description: + - When supplied, this argument will restrict the facts collected + to a given subset. Possible values for this argument include + all and the resources like lacp_interfaces, vlans etc. + 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 + version_added: "2.9" +""" + +EXAMPLES = """ +# Gather all facts +- ios_facts: + gather_subset: all + gather_network_resources: all +# Collect only the ios facts +- ios_facts: + gather_subset: + - !all + - !min + gather_network_resources: + - interfaces +# Do not collect ios facts +- ios_facts: + gather_network_resources: + - "!interfaces" +# Collect ios and minimal default facts +- ios_facts: + gather_subset: min + gather_network_resources: interfaces +""" + +RETURN = """ +See the respective resource module parameters for the tree. +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.network.ios.argspec.facts.facts import FactsArgs +from ansible.module_utils.network.ios.facts.facts import Facts + + +def main(): + """ + Main entry point for module execution + + :returns: ansible_facts + """ + module = AnsibleModule(argument_spec=FactsArgs.argument_spec, + supports_check_mode=True) + warnings = ['default value for `gather_subset` ' + 'will be changed to `min` from `!config` v2.11 onwards'] + + result = Facts(module).get_facts() + + ansible_facts, additional_warnings = result + warnings.extend(additional_warnings) + + module.exit_json(ansible_facts=ansible_facts, warnings=warnings) + + +if __name__ == '__main__': + main() diff --git a/library/ios_vlans.py b/library/ios_vlans.py new file mode 100644 index 0000000..95d924b --- /dev/null +++ b/library/ios_vlans.py @@ -0,0 +1,199 @@ +#!/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' +} + +NETWORK_OS = "ios" +RESOURCE = "vlans" +COPYRIGHT = "Copyright 2019 Red Hat" + +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 + options: + config: + description: A dictionary of VLANs options + type: list + elements: dict + suboptions: + name: + description: + - Ascii name of the VLAN. + type: str + vlan_id: + description: + - ID of the VLAN. Range 1-4094 + type: int + required: True + mtu: + description: + - VLAN Maximum Transmission Unit. Range 576-18190. + type: int + state: + description: + - Operational state of the VLAN + type: str + choices: + - active + - suspend + shutdown: + description: + - Shutdown VLAN switching. + type: bool + state: + description: + - The state the configuration should be left in + type: str + choices: + - merged + - replaced + - overridden + - deleted + default: merged +""" +EXAMPLES = """ +# Using deleted + + + +- name: Configure vlans + myos_interfaces: + operation: deleted + + + + +# Using merged + + + +- name: Configure vlans + nxos_interfaces: + config: + - name: Ethernet1/1 + description: 'Configured by Ansible' + enable: True + - name: Ethernet1/2 + description: 'Configured by Ansible' + enable: False + operation: merged + + + + +# Using overridden + + + +- name: Configure vlans + myos_interfaces: + config: + - name: Ethernet1/1 + description: 'Configured by Ansible' + enable: True + - name: Ethernet1/2 + description: 'Configured by Ansible' + enable: False + operation: overridden + + + + +# Using replaced + + + +- name: Configure vlans + nxos_interfaces: + config: + - name: Ethernet1/1 + description: 'Configured by Ansible' + enable: True + - name: Ethernet1/2 + description: 'Configured by Ansible' + enable: False + operation: replaced + + + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + 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 + 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: ['command 1', 'command 2', 'command 3'] +""" + + +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 +import q + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + q("ios_vlans") + module = AnsibleModule(argument_spec=VlansArgs.argument_spec, + supports_check_mode=True) + + result = Vlans(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/module_utils/__init__.py b/module_utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/module_utils/__init__.pyc b/module_utils/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..48d15cab480b1cefcaa5a1a391588afaf27412c7 GIT binary patch literal 111 zcmZSn%*&;tr4*aY00oRd+5w1*S%5?e14FO|NW@PANHCxg#S%cV-29Z%oYeTzlFXc9 e{rLFIyv&mLc)fzk5)PoG4Mf%sWJEC#GXMZm4ieJ< literal 0 HcmV?d00001 diff --git a/module_utils/network/__init__.py b/module_utils/network/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/module_utils/network/__init__.pyc b/module_utils/network/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6f75633c91a4c3463023f32a662da36ac764491 GIT binary patch literal 119 zcmZSn%*&;tr4*aY00oRd+5w1*S%5?e14FO|NW@PANHCxg#R@>N-29Z%oYeTzlFXc9 m{k+tY^8BJ~{rLFIyv&mLc)fzk5)Pmu8;B}9kWs}z%m4r)dlnP` literal 0 HcmV?d00001 diff --git a/module_utils/network/ios/__init__.py b/module_utils/network/ios/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/module_utils/network/ios/__init__.pyc b/module_utils/network/ios/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..58061c057c9e61e4affd844c915668a43161c7c4 GIT binary patch literal 123 zcmZSn%*&;tr4*aY00oRd+5w1*S%5?e14FO|NW@PANHCxg#VSCt-29Z%oYeTzlFXc9 q{k+tY^8BJ~{mlGg{rLFIyv&mLc)fzk5)Pn38;D9fka5L8%m4t7rx%g{ literal 0 HcmV?d00001 diff --git a/module_utils/network/ios/argspec/__init__.py b/module_utils/network/ios/argspec/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/module_utils/network/ios/argspec/facts/__init__.py b/module_utils/network/ios/argspec/facts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/module_utils/network/ios/argspec/facts/facts.py b/module_utils/network/ios/argspec/facts/facts.py new file mode 100644 index 0000000..04d3e2f --- /dev/null +++ b/module_utils/network/ios/argspec/facts/facts.py @@ -0,0 +1,28 @@ +#!/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) +""" +The arg spec for the ios facts module. +""" + + +class FactsArgs(object): # pylint: disable=R0903 + """ The arg spec for the ios facts module + """ + + def __init__(self, **kwargs): + pass + + choices = [ + 'all', + 'interfaces', + ] + + argument_spec = { + 'gather_subset': dict(default=['!config'], type='list'), + 'gather_network_resources': dict(default=['all'], + choices=choices, + type='list'), + } diff --git a/module_utils/network/ios/argspec/vlans/__init__.py b/module_utils/network/ios/argspec/vlans/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/module_utils/network/ios/argspec/vlans/vlans.py b/module_utils/network/ios/argspec/vlans/vlans.py new file mode 100644 index 0000000..44e5f37 --- /dev/null +++ b/module_utils/network/ios/argspec/vlans/vlans.py @@ -0,0 +1,46 @@ +#!/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 arg spec for the ios_vlans module +""" + + +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}, + 'state':{'type': 'str', 'choices':['active', 'suspend']}, + 'shutdown': {type: bool}}, + 'type': 'list'}, + 'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'], + 'default': 'merged', + 'type': 'str'}} diff --git a/module_utils/network/ios/config/__init__.py b/module_utils/network/ios/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/module_utils/network/ios/config/__init__.pyc b/module_utils/network/ios/config/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b101587a3dee1095b36fd37ad9f9f0038209aae2 GIT binary patch literal 130 zcmZSn%*&;tr4*aY00oRd+5w1*S%5?e14FO|NW@PANHCxg#o9oz-29Z%oYeTzlFXc9 x{k+tY^8BJ~{mlGg{p9?-w9It<`1s7c%#!$cy@JXT4xoA)hz2{5sl`Cd003X08a@C3 literal 0 HcmV?d00001 diff --git a/module_utils/network/ios/config/vlans/__init__.py b/module_utils/network/ios/config/vlans/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/module_utils/network/ios/config/vlans/__init__.pyc b/module_utils/network/ios/config/vlans/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f102dac250d632122ab9030fbea0a49e9b62c25a GIT binary patch literal 136 zcmZSn%*&;tr4*aY00oRd+5w1*S%5?e14FO|NW@PANHCxg#fCt!-29Z%oYeTzlFXc9 z{k+tY^8BJ~{mlGg{p9?-w9ItJSv@_{TH2K@6X6IXb*ZwdE2}f} z%dE_5{(IPeee2rKVpaV#@&B86%-d)ZrFPJIDru<9Q`3gpX(*3$Q)NvxZK<7>qgyI# zt7%8=bR6AQSy%0JRnk$qp~hIzRlii~ky1b1Sy4$_ZI4#C%X?`0Z|!M6ElhYfi*n<~ zS!7KA`_@m5pXR>Z!~b!bY5(4yE;VTF$Mdq(x%J~BAE%Rf8QHYR{ZVB6B#rI$K32rV zbQYC5@ogbXW@T|VO+cTr#*cEmOWwtF%D&OIpX*o~6O|9hWw%W8$p>tYS5muB-RbvaRlS!%2sXk*~bD7NN~W%oza zP;ILQMsDFTe@CNXE2tT5qq5ip=4#SZ6OVRETC~7Uo7!imL*uXmD~WMvm=zi6Dcx5| zkNrnjdqK$lf*c5WVL=Xs94yF-LJk+?s*o2K zaM7##uyljsJ;X~*WxlN@5EH#^5z_ny9R2TVE0A7?&a7GYN2j{i^}g7Hy5B18gPGp) zza{Pl*Mo-~nbdY(=4MMwce1C#QKOUA+N<~`AUxg8bXX}ZEVU`-B`hjt5xtG5u0Sk7 zj|rUxY!Cg3wxP&khomCNQ-)@~3wO3JEk_<_PEBL+h*}TC59x4@6&Z*3qB2JWFm?^p z5@&9=b6MZM0FM0d_jCOe#tq|y>a2TLypGp$$e{Q&GX4r8^#~4F{RSWafIm_MLe+5? z0TeLUQilNk0yT#Kj`IMFmPWADR{I@g#?tSq{S_{tkL18#j}C)fZi2utiH5pK96Ii8 z1&$nTVpQI|TK-ubVi-irEyLa*+VaA-7}883f&ZQRI-Vm~5`fO0PX#!jWu94oTo%)c zjjx3>U|M6;(!TLtluvYGSJ-z!--ti&>TywOL0dFNIFUCN)*sOvGWTYb0aSzw!*y_l z<1~!EZxQ+>8eu+k{vKT5G}p#^IzAv`wdEM+am?@l6Iv&TGH#9Veyf~o2Y10FH*g{b zA3--Dq{VCWeS8Gq`51--^_8rTO`Z?77qPihuC82g6C=h)qdG&c=k*!`??u3N0PqgI zeq+$+dwp++R-!WLJ}J_^DV&2#YPFU z73ln9X~s02AfNewJg4TyPjx?4j84ze6G#$JNR*`;s zcB=PyO;&Ve#lKEnA;GfR19H_V@lOcmZCztInjv?cl_^0np63!O{9UAsiI1e0Kyq2; zBEvlrHpQ?$Ew&{uo(K^Cw9|OYEvKE`@+UjYms!Gds7qpTaaWgRnj|_82;ks4 z8aW#hs2}ONOYd~KI3`Lx%aB_pE+Er?g3odZQT;gY3gc&tVj{D#Y+sbjmk968RN z;ePp5!RJ8A=N#Z9niz@x=X1JsaQrbIL+?|)HALwb;C7F$u;}VSvBii_1S2ZUN5nD5w3B^VT<~ZUYzK@ zOJhz-KI(U$Jw%y<{p!gT`M0wwvI>LGM3beS0`X^1(^93y__*ea-F+mC=6|TrFG4fF zJ*H45HH+sqXBb_Oq#$EC=&JIQQlCjFhe2e!th+FWq917vqV0>U#W3KC_&X_9E5lPQ zFZ|CKcRRtPDa{Qk7dbJIK%gLTHyf;E>namzH0qIQa{l0EKLJjt)M*k_FLHwxGVNYg z9KXjKF26%jaK*di^1EZI_y%72Fje5lahiIgQVVoam8S`BaTu|kR-_wz1$1x&jmRH- zmE`Me&K=OD>l>4qUWnwa71&QHvM0aK|s(^0vU;V&3E zCc{X|bV?P5c{J5wD2ZTL)vq9#cZFe6#9 zZTE^K;4vTYL<$&914e-0YizdIyus!s8fQv$p8n2|;JA#(Ttm}ocA8DE*XlL#JLr9^ zH|)K%dZGI5u69>@Bl> 1: + config['state'] = conf[2].split('/')[0] + config['shutdown'] = True + else: + config['state'] = conf[2] + elif vlan_info == 'Type' and 'Type' not in conf: + conf = filter(None, conf.split(' ')) + q("Type: ", conf) + config['mtu'] = conf[3] + # config['name'] = normalize_interface(intf) + # port_priority = utils.parse_conf_arg(conf, 'lacp port-priority') + # config['port_priority'] = port_priority + + return utils.remove_empties(config) diff --git a/module_utils/network/ios/utils/__init__.py b/module_utils/network/ios/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/module_utils/network/ios/utils/utils.py b/module_utils/network/ios/utils/utils.py new file mode 100644 index 0000000..26cf801 --- /dev/null +++ b/module_utils/network/ios/utils/utils.py @@ -0,0 +1,100 @@ +#!/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) + +# utils + + +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 + """ + if not name: + return + + def _get_number(name): + digits = '' + for char in name: + if char.isdigit() or char in '/.': + digits += char + return digits + + if name.lower().startswith('gi'): + if_type = 'GigabitEthernet' + elif name.lower().startswith('te'): + if_type = 'TenGigabitEthernet' + elif name.lower().startswith('fa'): + if_type = 'FastEthernet' + elif name.lower().startswith('fo'): + if_type = 'FortyGigabitEthernet' + elif name.lower().startswith('long'): + if_type = 'LongReachEthernet' + elif name.lower().startswith('et'): + if_type = 'Ethernet' + elif name.lower().startswith('vl'): + if_type = 'Vlan' + elif name.lower().startswith('lo'): + if_type = 'loopback' + elif name.lower().startswith('po'): + if_type = 'port-channel' + elif name.lower().startswith('nv'): + if_type = 'nve' + elif name.lower().startswith('twe'): + if_type = 'TwentyFiveGigE' + elif name.lower().startswith('hu'): + if_type = 'HundredGigE' + else: + if_type = None + + number_list = name.split(' ') + if len(number_list) == 2: + number = number_list[-1].strip() + else: + number = _get_number(name) + + if if_type: + proper_interface = if_type + number + else: + proper_interface = name + + return proper_interface + + +def get_interface_type(interface): + """Gets the type of interface + """ + + if interface.upper().startswith('GI'): + return 'GigabitEthernet' + elif interface.upper().startswith('TE'): + return 'TenGigabitEthernet' + elif interface.upper().startswith('FA'): + return 'FastEthernet' + elif interface.upper().startswith('FO'): + return 'FortyGigabitEthernet' + elif interface.upper().startswith('LON'): + return 'LongReachEthernet' + elif interface.upper().startswith('ET'): + return 'Ethernet' + elif interface.upper().startswith('VL'): + return 'Vlan' + elif interface.upper().startswith('LO'): + return 'loopback' + elif interface.upper().startswith('PO'): + return 'port-channel' + elif interface.upper().startswith('NV'): + return 'nve' + elif interface.upper().startswith('TWE'): + return 'TwentyFiveGigE' + elif interface.upper().startswith('HU'): + return 'HundredGigE' + else: + return 'unknown' From 0fe072c957ca39de74a9db330813bea85f82ce26 Mon Sep 17 00:00:00 2001 From: Sumit Jaiswal Date: Tue, 9 Jul 2019 17:14:26 +0530 Subject: [PATCH 02/11] ios vlans update Signed-off-by: Sumit Jaiswal --- library/ios_vlans.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/ios_vlans.py b/library/ios_vlans.py index 95d924b..122de7a 100644 --- a/library/ios_vlans.py +++ b/library/ios_vlans.py @@ -179,7 +179,7 @@ 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 -import q + def main(): """ @@ -187,7 +187,6 @@ def main(): :returns: the result form module invocation """ - q("ios_vlans") module = AnsibleModule(argument_spec=VlansArgs.argument_spec, supports_check_mode=True) From 44eddb3576b1a73141147d50bd5de0ab60672fcd Mon Sep 17 00:00:00 2001 From: Sumit Jaiswal Date: Tue, 9 Jul 2019 17:14:26 +0530 Subject: [PATCH 03/11] ios vlans update Signed-off-by: Sumit Jaiswal --- module_utils/network/ios/argspec/vlans/vlans.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module_utils/network/ios/argspec/vlans/vlans.py b/module_utils/network/ios/argspec/vlans/vlans.py index 44e5f37..da4f3ee 100644 --- a/module_utils/network/ios/argspec/vlans/vlans.py +++ b/module_utils/network/ios/argspec/vlans/vlans.py @@ -39,7 +39,7 @@ def __init__(self, **kwargs): 'vlan_id': {'required': True, 'type': int}, 'mtu': {'type': int}, 'state':{'type': 'str', 'choices':['active', 'suspend']}, - 'shutdown': {type: bool}}, + 'shutdown': {'type': bool}}, 'type': 'list'}, 'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'], 'default': 'merged', From 4e9153f48795379bc7beef92e8bf272ec88c94f1 Mon Sep 17 00:00:00 2001 From: Sumit Jaiswal Date: Tue, 9 Jul 2019 17:14:26 +0530 Subject: [PATCH 04/11] ios vlans update Signed-off-by: Sumit Jaiswal --- .../network/ios/config/vlans/vlans.py | 137 +++++++++++++++++- 1 file changed, 129 insertions(+), 8 deletions(-) diff --git a/module_utils/network/ios/config/vlans/vlans.py b/module_utils/network/ios/config/vlans/vlans.py index 88f0cf7..dcd63b5 100644 --- a/module_utils/network/ios/config/vlans/vlans.py +++ b/module_utils/network/ios/config/vlans/vlans.py @@ -10,10 +10,13 @@ necessary to bring the current configuration to it's desired end-state is created """ + +from ansible.module_utils.six import iteritems + 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 -import q + class Vlans(ConfigBase): """ @@ -117,6 +120,26 @@ def _state_replaced(**kwargs): to the desired configuration """ commands = [] + want = kwargs['want'] + have = kwargs['have'] + + check = False + for each in want: + for every in have: + if every['vlan_id'] == each['vlan_id']: + check = True + break + else: + continue + if check: + kwargs = {'want': each, 'have': every} + else: + kwargs = {'want': each, 'have': {}} + commands.extend(Vlans.clear_interface(**kwargs)) + commands.extend(Vlans.set_interface(**kwargs)) + # Remove the duplicate interface call + commands = Vlans._remove_duplicate_interface(commands) + return commands @staticmethod @@ -128,6 +151,31 @@ def _state_overridden(**kwargs): to the desired configuration """ commands = [] + want = kwargs['want'] + have = kwargs['have'] + + check = False + for every in have: + for each in want: + if each['vlan_id'] == every['vlan_id']: + check = True + 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': each, 'have': every} + commands.extend(Vlans.clear_interface(**kwargs)) + continue + if check: + kwargs = {'want': each, 'have': every} + else: + kwargs = {'want': each, 'have': {}} + commands.extend(Vlans.clear_interface(**kwargs)) + commands.extend(Vlans.set_interface(**kwargs)) + # Remove the duplicate interface call + commands = Vlans._remove_duplicate_interface(commands) + return commands @staticmethod @@ -139,6 +187,23 @@ def _state_merged(**kwargs): the current configuration """ commands = [] + want = kwargs['want'] + have = kwargs['have'] + + 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: + kwargs = {'want': each, 'have': every} + else: + kwargs = {'want': each, 'have': {}} + commands.extend(Vlans.set_interface(**kwargs)) + return commands @staticmethod @@ -150,28 +215,80 @@ def _state_deleted(**kwargs): of the provided objects """ commands = [] + want = kwargs['want'] + have = kwargs['have'] + + 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: + kwargs = {'want': each, 'have': every} + else: + kwargs = {'want': each, 'have': {}} + commands.extend(Vlans.clear_interface(**kwargs)) + return commands @staticmethod - def _remove_command_from_interface(interface, cmd, commands): - if interface not in commands: - commands.insert(0, interface) - commands.append('no %s' % cmd) + def _remove_command_from_interface(vlan, commands): + commands.append('no %s' % vlan) return commands @staticmethod - def _add_command_to_interface(interface, cmd, commands): - if interface not in commands: - commands.insert(0, interface) + def _add_command_to_interface(vlan_id, cmd, commands): + if vlan_id not in commands: + commands.insert(0, vlan_id) if cmd not in commands: commands.append(cmd) + @staticmethod + def _remove_duplicate_interface(commands): + # Remove duplicate interface from commands + set_cmd = [] + for each in commands: + if 'interface' in each: + interface = each + if interface not in set_cmd: + set_cmd.append(each) + else: + set_cmd.append(each) + + return set_cmd + @staticmethod def set_interface(**kwargs): # Set the interface config based on the want and have config commands = [] want = kwargs['want'] have = kwargs['have'] + vlan = 'vlan {}'.format(want.get('vlan_id')) + + # Get the diff b/w want n have + want_dict = set(tuple({k: v for k, v in iteritems(want) if v is not None}.items())) + have_dict = set(tuple({k: v for k, v in iteritems(have) if v is not None}.items())) + 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') + if name: + cmd = 'name {}'.format(name) + Vlans._add_command_to_interface(vlan, cmd, commands) + if state: + cmd = 'state {}'.format(state) + Vlans._add_command_to_interface(vlan, cmd, commands) + if shutdown: + Vlans._add_command_to_interface(vlan, 'shutdown', commands) + if mtu: + cmd = 'mtu {}'.format(mtu) + Vlans._add_command_to_interface(vlan, cmd, commands) return commands @@ -181,5 +298,9 @@ def clear_interface(**kwargs): commands = [] want = kwargs['want'] have = kwargs['have'] + vlan = 'vlan {}'.format(have.get('vlan_id')) + + if have.get('vlan_id') and have.get('vlan_id') != want.get('vlan_id'): + Vlans._remove_command_from_interface(vlan, commands) return commands From 813224a89bf6a01ffbe3910fc791fa5dba97738c Mon Sep 17 00:00:00 2001 From: Sumit Jaiswal Date: Tue, 9 Jul 2019 17:14:26 +0530 Subject: [PATCH 05/11] ios vlans update Signed-off-by: Sumit Jaiswal --- module_utils/network/ios/facts/vlans/vlans.py | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/module_utils/network/ios/facts/vlans/vlans.py b/module_utils/network/ios/facts/vlans/vlans.py index 86154af..968c58a 100644 --- a/module_utils/network/ios/facts/vlans/vlans.py +++ b/module_utils/network/ios/facts/vlans/vlans.py @@ -9,12 +9,12 @@ for a given resource, parsed, and the facts tree is populated based on the configuration. """ -import re + from copy import deepcopy from ansible.module_utils.network.common import utils from ansible.module_utils.network.ios.argspec.vlans.vlans import VlansArgs -import q + class VlansFacts(object): """ The ios vlans fact class @@ -46,13 +46,14 @@ def populate_facts(self, connection, ansible_facts, data=None): pass objs = [] + mtu_objs = [] + final_objs = [] if not data: data = connection.get('show vlan') # operate on a collection of resource x config = data.split('\n') - q(config) - # + # Get individual vlan configs separately vlan_info = '' for conf in config: if 'Name' in conf: @@ -63,17 +64,19 @@ def populate_facts(self, connection, ansible_facts, data=None): vlan_info = 'Remote' if conf and not ' ' in filter(None,conf.split('-')): obj = self.render_config(self.generated_spec, conf, vlan_info) - if obj: + if 'mtu' in obj: + mtu_objs.append(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) facts = {} - - if objs: + if final_objs: facts['vlans'] = [] - # params = utils.validate_config(self.argument_spec, {'config': objs}) - - params = {'config': objs} - + params = {'config': final_objs} for cfg in params['config']: facts['vlans'].append(cfg) ansible_facts['ansible_network_resources'].update(facts) @@ -94,20 +97,18 @@ def render_config(self, spec, conf, vlan_info): if vlan_info == 'Name' and 'Name' not in conf: conf = filter(None, conf.split(' ')) - q("Name: ", conf) - config['vlan_id'] = conf[0] + config['vlan_id'] = int(conf[0]) config['name'] = conf[1] if len(conf[2].split('/')) > 1: - config['state'] = conf[2].split('/')[0] + if conf[2].split('/')[0] == 'sus': + config['state'] = 'suspend' + elif conf[2].split('/')[0] == 'act': + config['state'] = 'active' config['shutdown'] = True else: config['state'] = conf[2] elif vlan_info == 'Type' and 'Type' not in conf: conf = filter(None, conf.split(' ')) - q("Type: ", conf) - config['mtu'] = conf[3] - # config['name'] = normalize_interface(intf) - # port_priority = utils.parse_conf_arg(conf, 'lacp port-priority') - # config['port_priority'] = port_priority + config['mtu'] = int(conf[3]) return utils.remove_empties(config) From 021509294da91a667f0a6204e08bc32ad1f1c7a7 Mon Sep 17 00:00:00 2001 From: Sumit Jaiswal Date: Thu, 11 Jul 2019 15:54:04 +0530 Subject: [PATCH 06/11] ios vlans update Signed-off-by: Sumit Jaiswal --- library/ios_vlans.py | 290 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 244 insertions(+), 46 deletions(-) diff --git a/library/ios_vlans.py b/library/ios_vlans.py index 122de7a..fcd2bcb 100644 --- a/library/ios_vlans.py +++ b/library/ios_vlans.py @@ -73,6 +73,10 @@ choices: - active - suspend + remote_span: + description: + - Configure as Remote SPAN VLAN + type: bool shutdown: description: - Shutdown VLAN switching. @@ -91,68 +95,262 @@ EXAMPLES = """ # Using deleted - - -- name: Configure vlans - myos_interfaces: - operation: 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 + - vlan_id: 30 + 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 # Using merged - - -- name: Configure vlans - nxos_interfaces: +# 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: Ethernet1/1 - description: 'Configured by Ansible' - enable: True - - name: Ethernet1/2 - description: 'Configured by Ansible' - enable: False - operation: merged - - - + - name: Vlan_10 + vlan_id: 10 + state: active + shutdown: False + remote_span: 10 + - name: Vlan_20 + vlan_id: 20 + mtu: 610 + state: active + shutdown: True + - name: Vlan_30 + vlan_id: 30 + state: suspend + shutdown: True + 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: Configure vlans - myos_interfaces: +- name: Override device configuration of all VLANs with provided configuration + ios_vlans: config: - - name: Ethernet1/1 - description: 'Configured by Ansible' - enable: True - - name: Ethernet1/2 - description: 'Configured by Ansible' - enable: False - operation: overridden - - + - 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: Configure vlans - nxos_interfaces: +- name: Replaces device configuration of listed VLANs with provided configuration + ios_vlans: config: - - name: Ethernet1/1 - description: 'Configured by Ansible' - enable: True - - name: Ethernet1/2 - description: 'Configured by Ansible' - enable: False - operation: replaced - - - + - name: Test_VLAN20 + vlan_id: 20 + mtu: 700 + shutdown: False + - 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 """ RETURN = """ From 1350860236172c8ad2c474abb616843ebea2de3b Mon Sep 17 00:00:00 2001 From: Sumit Jaiswal Date: Thu, 11 Jul 2019 15:54:04 +0530 Subject: [PATCH 07/11] ios vlans update Signed-off-by: Sumit Jaiswal --- module_utils/network/ios/argspec/vlans/vlans.py | 1 + 1 file changed, 1 insertion(+) diff --git a/module_utils/network/ios/argspec/vlans/vlans.py b/module_utils/network/ios/argspec/vlans/vlans.py index da4f3ee..4d2347e 100644 --- a/module_utils/network/ios/argspec/vlans/vlans.py +++ b/module_utils/network/ios/argspec/vlans/vlans.py @@ -38,6 +38,7 @@ def __init__(self, **kwargs): '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': bool}}, 'type': 'list'}, From e7a0a840b8d922ab57533a77803655c0ee544c69 Mon Sep 17 00:00:00 2001 From: Sumit Jaiswal Date: Thu, 11 Jul 2019 15:54:04 +0530 Subject: [PATCH 08/11] ios vlans update Signed-off-by: Sumit Jaiswal --- .../network/ios/config/vlans/vlans.py | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/module_utils/network/ios/config/vlans/vlans.py b/module_utils/network/ios/config/vlans/vlans.py index dcd63b5..e712c5f 100644 --- a/module_utils/network/ios/config/vlans/vlans.py +++ b/module_utils/network/ios/config/vlans/vlans.py @@ -101,7 +101,7 @@ def set_state(self, want, have): kwargs = {'want': want, 'have': have} commands = self._state_overridden(**kwargs) elif state == 'deleted': - kwargs = {'want': want, 'have': have} + kwargs = {'want': want, 'have': have, 'state': state} commands = self._state_deleted(**kwargs) elif state == 'merged': kwargs = {'want': want, 'have': have} @@ -217,6 +217,7 @@ def _state_deleted(**kwargs): commands = [] want = kwargs['want'] have = kwargs['have'] + state = kwargs['state'] check = False for each in want: @@ -227,16 +228,21 @@ def _state_deleted(**kwargs): else: continue if check: - kwargs = {'want': each, 'have': every} + kwargs = {'want': each, 'have': every, 'state': state} else: - kwargs = {'want': each, 'have': {}} + kwargs = {'want': each, 'have': {}, 'state': state} commands.extend(Vlans.clear_interface(**kwargs)) return commands @staticmethod - def _remove_command_from_interface(vlan, commands): - commands.append('no %s' % vlan) + def _remove_command_from_interface(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 @staticmethod @@ -251,9 +257,9 @@ def _remove_duplicate_interface(commands): # Remove duplicate interface from commands set_cmd = [] for each in commands: - if 'interface' in each: - interface = each - if interface not in set_cmd: + if 'vlan' in each: + vlan = each + if vlan not in set_cmd: set_cmd.append(each) else: set_cmd.append(each) @@ -278,17 +284,20 @@ def set_interface(**kwargs): 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 {}'.format(name) Vlans._add_command_to_interface(vlan, cmd, commands) if state: cmd = 'state {}'.format(state) Vlans._add_command_to_interface(vlan, cmd, commands) - if shutdown: - Vlans._add_command_to_interface(vlan, 'shutdown', commands) if mtu: cmd = 'mtu {}'.format(mtu) Vlans._add_command_to_interface(vlan, cmd, commands) + if remote_span: + Vlans._add_command_to_interface(vlan, 'remote-span', commands) + if shutdown: + Vlans._add_command_to_interface(vlan, 'shutdown', commands) return commands @@ -298,9 +307,19 @@ def clear_interface(**kwargs): commands = [] want = kwargs['want'] have = kwargs['have'] + state = kwargs['state'] vlan = 'vlan {}'.format(have.get('vlan_id')) - if have.get('vlan_id') and have.get('vlan_id') != want.get('vlan_id'): - Vlans._remove_command_from_interface(vlan, commands) + if have.get('vlan_id') and (have.get('vlan_id') != want.get('vlan_id') or state == 'deleted'): + Vlans._remove_command_from_interface(vlan, 'vlan', commands) + else: + if have.get('mtu') != want.get('mtu'): + Vlans._remove_command_from_interface(vlan, 'mtu', commands) + if have.get('remote_span') != want.get('remote_span') and want.get('remote_span'): + Vlans._remove_command_from_interface(vlan, 'remote-span', commands) + if have.get('shutdown') != want.get('shutdown') and want.get('shutdown'): + Vlans._remove_command_from_interface(vlan, 'shutdown', commands) + if have.get('state') != want.get('state') and want.get('state'): + Vlans._remove_command_from_interface(vlan, 'state', commands) return commands From 3d06c5db4dc359ed5a7ca39860123675d30f3fcc Mon Sep 17 00:00:00 2001 From: Sumit Jaiswal Date: Thu, 11 Jul 2019 15:54:04 +0530 Subject: [PATCH 09/11] ios vlans update Signed-off-by: Sumit Jaiswal --- module_utils/network/ios/facts/vlans/vlans.py | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/module_utils/network/ios/facts/vlans/vlans.py b/module_utils/network/ios/facts/vlans/vlans.py index 968c58a..5ee0f86 100644 --- a/module_utils/network/ios/facts/vlans/vlans.py +++ b/module_utils/network/ios/facts/vlans/vlans.py @@ -47,11 +47,11 @@ def populate_facts(self, connection, ansible_facts, data=None): 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 = '' @@ -66,6 +66,8 @@ def populate_facts(self, connection, ansible_facts, data=None): 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 @@ -73,6 +75,14 @@ def populate_facts(self, connection, ansible_facts, data=None): o.update(m) final_objs.append(o) + # Appending Remote Span value to related VLAN: + 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'] = [] @@ -106,9 +116,21 @@ def render_config(self, spec, conf, vlan_info): config['state'] = 'active' config['shutdown'] = True else: - config['state'] = conf[2] + if conf[2] == 'suspended': + config['state'] = 'suspend' 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('-')[0]) == 2 or len(conf.split(',')) > 1: + 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) From 1c21f227713fb5e1e3c96a4680ddc4b17d1f8b7a Mon Sep 17 00:00:00 2001 From: Sumit Jaiswal Date: Mon, 29 Jul 2019 12:01:26 +0530 Subject: [PATCH 10/11] fix override state --- library/ios_vlans.py | 109 ++++++++-------- .../network/ios/argspec/facts/facts.py | 2 +- .../network/ios/argspec/vlans/vlans.py | 2 +- .../network/ios/config/vlans/vlans.py | 117 ++++++++---------- module_utils/network/ios/facts/facts.py | 2 +- module_utils/network/ios/facts/vlans/vlans.py | 25 ++-- 6 files changed, 126 insertions(+), 131 deletions(-) diff --git a/library/ios_vlans.py b/library/ios_vlans.py index fcd2bcb..0e0b25c 100644 --- a/library/ios_vlans.py +++ b/library/ios_vlans.py @@ -41,56 +41,61 @@ 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 - options: - config: - description: A dictionary of VLANs options - type: list - elements: dict - suboptions: - name: - description: - - Ascii name of the VLAN. - type: str - vlan_id: - description: - - ID of the VLAN. Range 1-4094 - type: int - required: True - mtu: - description: - - VLAN Maximum Transmission Unit. Range 576-18190. - 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: bool +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 +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: - - The state the configuration should be left in + - Operational state of the VLAN type: str choices: - - merged - - replaced - - overridden - - deleted - default: merged + - 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 deleted @@ -183,17 +188,17 @@ - name: Vlan_10 vlan_id: 10 state: active - shutdown: False + shutdown: disabled remote_span: 10 - name: Vlan_20 vlan_id: 20 mtu: 610 state: active - shutdown: True + shutdown: enabled - name: Vlan_30 vlan_id: 30 state: suspend - shutdown: True + shutdown: enabled state: merged # After state: @@ -316,7 +321,7 @@ - name: Test_VLAN20 vlan_id: 20 mtu: 700 - shutdown: False + shutdown: disabled - vlan_id: 30 name: Test_VLAN30 mtu: 1000 @@ -357,12 +362,14 @@ 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. @@ -370,7 +377,7 @@ description: The set of commands pushed to the remote device. returned: always type: list - sample: ['command 1', 'command 2', 'command 3'] + sample: ['vlan 20', 'name vlan_20', 'mtu 600', 'remote-span'] """ diff --git a/module_utils/network/ios/argspec/facts/facts.py b/module_utils/network/ios/argspec/facts/facts.py index 04d3e2f..d917039 100644 --- a/module_utils/network/ios/argspec/facts/facts.py +++ b/module_utils/network/ios/argspec/facts/facts.py @@ -17,7 +17,7 @@ def __init__(self, **kwargs): choices = [ 'all', - 'interfaces', + 'vlans', ] argument_spec = { diff --git a/module_utils/network/ios/argspec/vlans/vlans.py b/module_utils/network/ios/argspec/vlans/vlans.py index 4d2347e..6c4519a 100644 --- a/module_utils/network/ios/argspec/vlans/vlans.py +++ b/module_utils/network/ios/argspec/vlans/vlans.py @@ -40,7 +40,7 @@ def __init__(self, **kwargs): 'mtu': {'type': int}, 'remote_span': {'type': bool}, 'state':{'type': 'str', 'choices':['active', 'suspend']}, - 'shutdown': {'type': bool}}, + 'shutdown': {'type': 'str', 'choices':['enabled', 'disabled']}}, 'type': 'list'}, 'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'], 'default': 'merged', diff --git a/module_utils/network/ios/config/vlans/vlans.py b/module_utils/network/ios/config/vlans/vlans.py index e712c5f..ca75c35 100644 --- a/module_utils/network/ios/config/vlans/vlans.py +++ b/module_utils/network/ios/config/vlans/vlans.py @@ -29,7 +29,7 @@ class Vlans(ConfigBase): ] gather_network_resources = [ - 'interfaces', + 'vlans', ] def __init__(self, module): @@ -98,21 +98,17 @@ def set_state(self, want, have): """ state = self._module.params['state'] if state == 'overridden': - kwargs = {'want': want, 'have': have} - commands = self._state_overridden(**kwargs) + commands = self._state_overridden(want, have, state) elif state == 'deleted': - kwargs = {'want': want, 'have': have, 'state': state} - commands = self._state_deleted(**kwargs) + commands = self._state_deleted(want, have, state) elif state == 'merged': - kwargs = {'want': want, 'have': have} - commands = self._state_merged(**kwargs) + commands = self._state_merged(want, have) elif state == 'replaced': - kwargs = {'want': want, 'have': have} - commands = self._state_replaced(**kwargs) + commands = self._state_replaced(want, have) return commands @staticmethod - def _state_replaced(**kwargs): + def _state_replaced(want, have): """ The command generator when state is replaced :rtype: A list @@ -120,8 +116,6 @@ def _state_replaced(**kwargs): to the desired configuration """ commands = [] - want = kwargs['want'] - have = kwargs['have'] check = False for each in want: @@ -135,15 +129,12 @@ def _state_replaced(**kwargs): kwargs = {'want': each, 'have': every} else: kwargs = {'want': each, 'have': {}} - commands.extend(Vlans.clear_interface(**kwargs)) commands.extend(Vlans.set_interface(**kwargs)) - # Remove the duplicate interface call - commands = Vlans._remove_duplicate_interface(commands) return commands @staticmethod - def _state_overridden(**kwargs): + def _state_overridden(want, have, state): """ The command generator when state is overridden :rtype: A list @@ -151,35 +142,29 @@ def _state_overridden(**kwargs): to the desired configuration """ commands = [] - want = kwargs['want'] - have = kwargs['have'] check = False - for every in have: - for each in want: + for each in want: + for every in have: if each['vlan_id'] == every['vlan_id']: check = True 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': each, 'have': every} - commands.extend(Vlans.clear_interface(**kwargs)) - continue + else: + # We didn't find a matching desired state, which means we can + # pretend we recieved an empty desired state. + kwargs = {'want': each, 'have': every, 'state': state} + commands.extend(Vlans.clear_interface(**kwargs)) + continue if check: kwargs = {'want': each, 'have': every} else: kwargs = {'want': each, 'have': {}} - commands.extend(Vlans.clear_interface(**kwargs)) commands.extend(Vlans.set_interface(**kwargs)) - # Remove the duplicate interface call - commands = Vlans._remove_duplicate_interface(commands) return commands @staticmethod - def _state_merged(**kwargs): + def _state_merged(want, have): """ The command generator when state is merged :rtype: A list @@ -187,8 +172,6 @@ def _state_merged(**kwargs): the current configuration """ commands = [] - want = kwargs['want'] - have = kwargs['have'] check = False for each in want: @@ -207,7 +190,7 @@ def _state_merged(**kwargs): return commands @staticmethod - def _state_deleted(**kwargs): + def _state_deleted(want, have, state): """ The command generator when state is deleted :rtype: A list @@ -215,23 +198,24 @@ def _state_deleted(**kwargs): of the provided objects """ commands = [] - want = kwargs['want'] - have = kwargs['have'] - state = kwargs['state'] - 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: - kwargs = {'want': each, 'have': every, 'state': state} - else: - kwargs = {'want': each, 'have': {}, 'state': state} - commands.extend(Vlans.clear_interface(**kwargs)) + 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: + kwargs = {'want': each, 'have': every, 'state': state} + commands.extend(Vlans.clear_interface(**kwargs)) + else: + for each in have: + kwargs = {'want': {}, 'have': each, 'state': state} + commands.extend(Vlans.clear_interface(**kwargs)) return commands @@ -253,18 +237,14 @@ def _add_command_to_interface(vlan_id, cmd, commands): commands.append(cmd) @staticmethod - def _remove_duplicate_interface(commands): - # Remove duplicate interface from commands - set_cmd = [] - for each in commands: - if 'vlan' in each: - vlan = each - if vlan not in set_cmd: - set_cmd.append(each) - else: - set_cmd.append(each) - - return set_cmd + def dict_diff(sample_dict): + # Generate a set with passed dictionary for comparison + test_dict = {} + for k, v in iteritems(sample_dict): + if v is not None: + test_dict.update({k: v}) + return_set = set(tuple(test_dict.items())) + return return_set @staticmethod def set_interface(**kwargs): @@ -275,8 +255,8 @@ def set_interface(**kwargs): vlan = 'vlan {}'.format(want.get('vlan_id')) # Get the diff b/w want n have - want_dict = set(tuple({k: v for k, v in iteritems(want) if v is not None}.items())) - have_dict = set(tuple({k: v for k, v in iteritems(have) if v is not None}.items())) + want_dict = Vlans.dict_diff(want) + have_dict = Vlans.dict_diff(have) diff = want_dict - have_dict if diff: @@ -296,8 +276,10 @@ def set_interface(**kwargs): Vlans._add_command_to_interface(vlan, cmd, commands) if remote_span: Vlans._add_command_to_interface(vlan, 'remote-span', commands) - if shutdown: + if shutdown == 'enabled': Vlans._add_command_to_interface(vlan, 'shutdown', commands) + elif shutdown == 'disabled': + Vlans._add_command_to_interface(vlan, 'no shutdown', commands) return commands @@ -310,9 +292,10 @@ def clear_interface(**kwargs): state = kwargs['state'] vlan = 'vlan {}'.format(have.get('vlan_id')) - if have.get('vlan_id') and (have.get('vlan_id') != want.get('vlan_id') or state == 'deleted'): + if have.get('vlan_id') and 'default' not in have.get('name')\ + and (have.get('vlan_id') != want.get('vlan_id') or state == 'deleted'): Vlans._remove_command_from_interface(vlan, 'vlan', commands) - else: + elif 'default' not in have.get('name'): if have.get('mtu') != want.get('mtu'): Vlans._remove_command_from_interface(vlan, 'mtu', commands) if have.get('remote_span') != want.get('remote_span') and want.get('remote_span'): diff --git a/module_utils/network/ios/facts/facts.py b/module_utils/network/ios/facts/facts.py index dd3127c..d381cc8 100644 --- a/module_utils/network/ios/facts/facts.py +++ b/module_utils/network/ios/facts/facts.py @@ -16,7 +16,7 @@ FACT_LEGACY_SUBSETS = {} FACT_RESOURCE_SUBSETS = dict( - interfaces=VlansFacts, + vlans=VlansFacts, ) diff --git a/module_utils/network/ios/facts/vlans/vlans.py b/module_utils/network/ios/facts/vlans/vlans.py index 5ee0f86..dac4822 100644 --- a/module_utils/network/ios/facts/vlans/vlans.py +++ b/module_utils/network/ios/facts/vlans/vlans.py @@ -76,19 +76,21 @@ def populate_facts(self, connection, ansible_facts, data=None): final_objs.append(o) # Appending Remote Span value to related VLAN: - 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 + 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 = {'config': final_objs} + params = utils.validate_config(self.argument_spec, {'config': objs}) + for cfg in params['config']: - facts['vlans'].append(cfg) + facts['vlans'].append(utils.remove_empties(cfg)) ansible_facts['ansible_network_resources'].update(facts) return ansible_facts @@ -114,15 +116,18 @@ def render_config(self, spec, conf, vlan_info): config['state'] = 'suspend' elif conf[2].split('/')[0] == 'act': config['state'] = 'active' - config['shutdown'] = True + 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('-')[0]) == 2 or len(conf.split(',')) > 1: + if len(conf.split(',')) > 1 or conf.isdigit(): remote_span_vlan = [] if len(conf.split(',')) > 1: remote_span_vlan = conf.split(',') From e803496043aefdbb354acc0c80e610a8c11cb7bf Mon Sep 17 00:00:00 2001 From: Sumit Jaiswal Date: Sat, 17 Aug 2019 19:10:43 +0530 Subject: [PATCH 11/11] fix vlans --- .../network/ios/config/vlans/vlans.py | 117 +++++++----------- module_utils/network/ios/utils/utils.py | 85 ++++++++++++- 2 files changed, 123 insertions(+), 79 deletions(-) diff --git a/module_utils/network/ios/config/vlans/vlans.py b/module_utils/network/ios/config/vlans/vlans.py index ca75c35..3b523b9 100644 --- a/module_utils/network/ios/config/vlans/vlans.py +++ b/module_utils/network/ios/config/vlans/vlans.py @@ -11,11 +11,14 @@ created """ -from ansible.module_utils.six import iteritems +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): @@ -107,8 +110,7 @@ def set_state(self, want, have): commands = self._state_replaced(want, have) return commands - @staticmethod - def _state_replaced(want, have): + def _state_replaced(self, want, have): """ The command generator when state is replaced :rtype: A list @@ -126,15 +128,13 @@ def _state_replaced(want, have): else: continue if check: - kwargs = {'want': each, 'have': every} + commands.extend(self._set_config(each, every)) else: - kwargs = {'want': each, 'have': {}} - commands.extend(Vlans.set_interface(**kwargs)) + commands.extend(self._set_config(each, dict())) return commands - @staticmethod - def _state_overridden(want, have, state): + def _state_overridden(self, want, have, state): """ The command generator when state is overridden :rtype: A list @@ -143,28 +143,20 @@ def _state_overridden(want, have, state): """ commands = [] - check = False - for each in want: - for every in have: + for each in have: + for every in want: if each['vlan_id'] == every['vlan_id']: - check = True break - else: - # We didn't find a matching desired state, which means we can - # pretend we recieved an empty desired state. - kwargs = {'want': each, 'have': every, 'state': state} - commands.extend(Vlans.clear_interface(**kwargs)) - continue - if check: - kwargs = {'want': each, 'have': every} else: - kwargs = {'want': each, 'have': {}} - commands.extend(Vlans.set_interface(**kwargs)) + # 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 - @staticmethod - def _state_merged(want, have): + def _state_merged(self, want, have): """ The command generator when state is merged :rtype: A list @@ -182,15 +174,13 @@ def _state_merged(want, have): else: continue if check: - kwargs = {'want': each, 'have': every} + commands.extend(self._set_config(each, every)) else: - kwargs = {'want': each, 'have': {}} - commands.extend(Vlans.set_interface(**kwargs)) + commands.extend(self._set_config(each, dict())) return commands - @staticmethod - def _state_deleted(want, have, state): + def _state_deleted(self, want, have, state): """ The command generator when state is deleted :rtype: A list @@ -210,17 +200,14 @@ def _state_deleted(want, have, state): check = False continue if check: - kwargs = {'want': each, 'have': every, 'state': state} - commands.extend(Vlans.clear_interface(**kwargs)) + commands.extend(self._clear_config(each, every, state)) else: for each in have: - kwargs = {'want': {}, 'have': each, 'state': state} - commands.extend(Vlans.clear_interface(**kwargs)) + commands.extend(self._clear_config(dict(), each, state)) return commands - @staticmethod - def _remove_command_from_interface(vlan, cmd, 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': @@ -229,34 +216,20 @@ def _remove_command_from_interface(vlan, cmd, commands): commands.append('no %s' % cmd) return commands - @staticmethod - def _add_command_to_interface(vlan_id, cmd, 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) - @staticmethod - def dict_diff(sample_dict): - # Generate a set with passed dictionary for comparison - test_dict = {} - for k, v in iteritems(sample_dict): - if v is not None: - test_dict.update({k: v}) - return_set = set(tuple(test_dict.items())) - return return_set - - @staticmethod - def set_interface(**kwargs): + def _set_config(self, want, have): # Set the interface config based on the want and have config commands = [] - want = kwargs['want'] - have = kwargs['have'] - vlan = 'vlan {}'.format(want.get('vlan_id')) + vlan = 'vlan {0}'.format(want.get('vlan_id')) # Get the diff b/w want n have - want_dict = Vlans.dict_diff(want) - have_dict = Vlans.dict_diff(have) + want_dict = dict_to_set(want) + have_dict = dict_to_set(have) diff = want_dict - have_dict if diff: @@ -266,43 +239,39 @@ def set_interface(**kwargs): mtu = dict(diff).get('mtu') remote_span = dict(diff).get('remote_span') if name: - cmd = 'name {}'.format(name) - Vlans._add_command_to_interface(vlan, cmd, commands) + cmd = 'name {0}'.format(name) + self.add_command_to_config_list(vlan, cmd, commands) if state: - cmd = 'state {}'.format(state) - Vlans._add_command_to_interface(vlan, cmd, commands) + cmd = 'state {0}'.format(state) + self.add_command_to_config_list(vlan, cmd, commands) if mtu: - cmd = 'mtu {}'.format(mtu) - Vlans._add_command_to_interface(vlan, cmd, commands) + cmd = 'mtu {0}'.format(mtu) + self.add_command_to_config_list(vlan, cmd, commands) if remote_span: - Vlans._add_command_to_interface(vlan, 'remote-span', commands) + self.add_command_to_config_list(vlan, 'remote-span', commands) if shutdown == 'enabled': - Vlans._add_command_to_interface(vlan, 'shutdown', commands) + self.add_command_to_config_list(vlan, 'shutdown', commands) elif shutdown == 'disabled': - Vlans._add_command_to_interface(vlan, 'no shutdown', commands) + self.add_command_to_config_list(vlan, 'no shutdown', commands) return commands - @staticmethod - def clear_interface(**kwargs): + def _clear_config(self, want, have, state): # Delete the interface config based on the want and have config commands = [] - want = kwargs['want'] - have = kwargs['have'] - state = kwargs['state'] - vlan = 'vlan {}'.format(have.get('vlan_id')) + 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'): - Vlans._remove_command_from_interface(vlan, 'vlan', commands) + self.remove_command_from_config_list(vlan, 'vlan', commands) elif 'default' not in have.get('name'): if have.get('mtu') != want.get('mtu'): - Vlans._remove_command_from_interface(vlan, 'mtu', commands) + self.remove_command_from_config_list(vlan, 'mtu', commands) if have.get('remote_span') != want.get('remote_span') and want.get('remote_span'): - Vlans._remove_command_from_interface(vlan, 'remote-span', commands) + self.remove_command_from_config_list(vlan, 'remote-span', commands) if have.get('shutdown') != want.get('shutdown') and want.get('shutdown'): - Vlans._remove_command_from_interface(vlan, 'shutdown', commands) + self.remove_command_from_config_list(vlan, 'shutdown', commands) if have.get('state') != want.get('state') and want.get('state'): - Vlans._remove_command_from_interface(vlan, 'state', commands) + self.remove_command_from_config_list(vlan, 'state', commands) return commands diff --git a/module_utils/network/ios/utils/utils.py b/module_utils/network/ios/utils/utils.py index 26cf801..946e9aa 100644 --- a/module_utils/network/ios/utils/utils.py +++ b/module_utils/network/ios/utils/utils.py @@ -7,11 +7,86 @@ # utils -def search_obj_in_list(name, lst): - for o in lst: - if o['name'] == name: - return o - return None +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible.module_utils.six import iteritems + + +def remove_command_from_config_list(interface, cmd, commands): + # To delete the passed config + if interface not in commands: + commands.insert(0, interface) + commands.append('no %s' % cmd) + return commands + + +def add_command_to_config_list(interface, cmd, commands): + # To set the passed config + if interface not in commands: + commands.insert(0, interface) + commands.append(cmd) + + +def dict_to_set(sample_dict): + # Generate a set with passed dictionary for comparison + test_dict = {} + for k, v in iteritems(sample_dict): + if v is not None: + if isinstance(v, list) and v: + 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.append(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) + elif isinstance(v, list): + v = tuple(v) + test_dict.update({k: v}) + return_set = set(tuple(test_dict.items())) + return return_set + + +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}) + return test_dict + + +def remove_duplicate_interface(commands): + # Remove duplicate interface from commands + set_cmd = [] + for each in commands: + if 'interface' in each: + if each not in set_cmd: + set_cmd.append(each) + else: + set_cmd.append(each) + + return set_cmd def normalize_interface(name):