diff --git a/.idea/libraries/R_User_Library.xml b/.idea/libraries/R_User_Library.xml
new file mode 100644
index 0000000..71f5ff7
--- /dev/null
+++ b/.idea/libraries/R_User_Library.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..7ba73c2
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..a82482f
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/network.iml b/.idea/network.iml
new file mode 100644
index 0000000..bf708e3
--- /dev/null
+++ b/.idea/network.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..cb329fe
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,508 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ id
+ after
+ from __future__
+ https://www.visa.go.kr/openPage.do?MENU_ID=10101&LANG_TYPE=EN
+ exit_json
+ int(
+ flowcontrol
+ interfaces
+ vlan
+ !/usr/bin/python
+ get_interfaces_facts
+ _clear_in
+ filter_dict_having_none_value
+ Interfaces.
+ re.
+ get_interface_type
+ normalize_interface
+ parse_conf_arg
+ clear_interface
+ l2protocol
+ l2ptotocol_type
+ l2protocol_
+ propagate
+ re.search
+ dot1q native vla
+ l2_protocol_diff
+ int
+ ios
+ import q
+ q(
+
+
+ state
+ name
+ _set_config
+ _clear_config
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1562520202828
+
+
+ 1562520202828
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/library/__init__.py b/library/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/library/iosxr_facts.py b/library/iosxr_facts.py
new file mode 100644
index 0000000..207665b
--- /dev/null
+++ b/library/iosxr_facts.py
@@ -0,0 +1,116 @@
+#!/usr/bin/python
+# -*- 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 module file for iosxr_facts
+"""
+
+from __future__ import absolute_import, division, print_function
+
+ANSIBLE_METADATA = {'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'network'}
+
+NETWORK_OS = "iosxr"
+RESOURCE = "facts"
+COPYRIGHT = "Copyright 2019 Red Hat"
+
+DOCUMENTATION = """
+---
+module: iosxr_facts
+version_added: 2.9
+short_description: Get facts about Cisco IOSXR devices.
+description:
+ - Collects facts from network devices running the iosxr 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 IOSXRv Version 6.1.3 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 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 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
+- iosxr_facts:
+ gather_subset: all
+ gather_network_resources: all
+
+# Collect only the iosxr facts
+- iosxr_facts:
+ gather_subset:
+ - !all
+ - !min
+ gather_network_resources:
+ - iosxr
+
+# Do not collect iosxr facts
+- iosxr_facts:
+ gather_network_resources:
+ - "!iosxr"
+
+# Collect iosxr and minimal default facts
+- iosxr_facts:
+ gather_subset: min
+ gather_network_resources: iosxr
+"""
+
+RETURN = """
+See the respective resource module parameters for the tree.
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.connection import Connection
+from ansible.module_utils.iosxr.facts.facts import Facts
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: ansible_facts
+ """
+ module = AnsibleModule(argument_spec=Facts.argument_spec,
+ supports_check_mode=True)
+ warnings = ['default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards']
+
+ connection = Connection(module._socket_path)
+ gather_subset = module.params['gather_subset']
+ gather_network_resources = module.params['gather_network_resources']
+ result = Facts().get_facts(module, connection, gather_subset, gather_network_resources)
+
+ try:
+ ansible_facts, warning = result
+ warnings.extend(warning)
+ except (TypeError, KeyError):
+ ansible_facts = result
+
+ module.exit_json(ansible_facts=ansible_facts, warnings=warnings)
+
+
+if __name__ == '__main__':
+ main()
+
diff --git a/library/iosxr_l2_interfaces.py b/library/iosxr_l2_interfaces.py
new file mode 100644
index 0000000..6ef5186
--- /dev/null
+++ b/library/iosxr_l2_interfaces.py
@@ -0,0 +1,421 @@
+#!/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 iosxr_interfaces
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+GENERATOR_VERSION = '1.0'
+
+ANSIBLE_METADATA = {'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'network'}
+
+
+DOCUMENTATION = """
+---
+ module: iosxr_l2_interfaces
+ version_added: 2.9
+ short_description: Manage Layer-2 interface on Cisco IOS-XR devices
+ description: This module provides declarative management of a Layer-2 interface on Cisco IOS-XR devices.
+ author: Sumit Jaiswal (@justjais)
+ options:
+ config:
+ description: A dictionary of Layer-2 interface options
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - Full name of the interface/sub-interface excluding any logical unit number,
+ e.g. GigabitEthernet0/0/0/1 or GigabitEthernet0/0/0/1.100.
+ type: str
+ required: True
+ native_vlan:
+ description:
+ - Configure a native VLAN ID for the trunk
+ - Refer to vendor documentation for valid vlan values.
+ type: str
+ l2transport:
+ description:
+ - Switchport mode access command to configure the interface as a layer 2 access
+ type: bool
+ l2protocol:
+ description:
+ - Configures Layer 2 protocol tunneling and protocol data unit (PDU) filtering on an interface.
+ suboptions:
+ cdp:
+ description:
+ - Cisco Discovery Protocol (CDP) tunneling and data unit parameters.
+ choices: ['drop','forward', 'tunnel']
+ type: str
+ pvst:
+ description:
+ - Configures the per-VLAN Spanning Tree Protocol (PVST) tunneling and data unit parameters.
+ choices: ['drop','forward', 'tunnel']
+ type: str
+ stp:
+ description:
+ - Spanning Tree Protocol (STP) tunneling and data unit parameters.
+ choices: ['drop','forward', 'tunnel']
+ type: str
+ vtp:
+ description:
+ - VLAN Trunk Protocol (VTP) tunneling and data unit parameters.
+ choices: ['drop','forward', 'tunnel']
+ type: str
+ q_vlan:
+ description:
+ - 802.1Q VLAN configuration.
+ - User can configure first (outer) and second (inner 802.1Q) VLAN ID on the subinterface and can pass outer
+ and inner VLAN ID as list.
+ - NOTE, that this option is valid only with respect to Sub-Interface and is not valid when configuring
+ for Interface.
+ type: list
+ propagate:
+ description:
+ - Propagate Layer 2 transport events. Note that it will work only when the I(l2tranport) option is set
+ to True.
+ type: bool
+ state:
+ choices:
+ - merged
+ - replaced
+ - overridden
+ - deleted
+ default: merged
+ description:
+ - The state the configuration should be left in
+ type: str
+"""
+
+EXAMPLES = """
+---
+# Using merged
+#
+# Before state:
+# -------------
+#
+# viosxr#show running-config interface
+# interface GigabitEthernet0/0/0/3
+# description Ansible Network
+# vrf custB
+# ipv4 address 10.10.0.2 255.255.255.0
+# duplex half
+# shutdown
+# !
+# interface GigabitEthernet0/0/0/4
+# description Test description
+# !
+
+- name: Merge provided configuration with device configuration
+ iosxr_l2_interfaces:
+ config:
+ - name: GigabitEthernet0/0/0/3
+ native_vlan: 20
+ - name: GigabitEthernet0/0/0/4
+ native_vlan: 40
+ l2transport: True
+ l2protocol:
+ - stp: tunnel
+ - name: GigabitEthernet0/0/0/3.900
+ l2transport: True
+ q_vlan:
+ - 20
+ - 40
+ state: merged
+
+# After state:
+# ------------
+#
+# viosxr#show running-config interface
+# interface GigabitEthernet0/0/0/3
+# description Ansible Network
+# vrf custB
+# ipv4 address 10.10.0.2 255.255.255.0
+# duplex half
+# shutdown
+# dot1q native vlan 20
+# !
+# interface GigabitEthernet0/0/0/4
+# description Test description
+# dot1q native vlan 10
+# l2transport
+# l2protocol stp tunnel
+# !
+# !
+# interface GigabitEthernet0/0/0/3.900 l2transport
+# dot1q vlan 20 40
+# !
+
+# Using replaced
+#
+# Before state:
+# -------------
+#
+# viosxr#show running-config interface
+# interface GigabitEthernet0/0/0/3
+# description Ansible Network
+# vrf custB
+# ipv4 address 10.10.0.2 255.255.255.0
+# duplex half
+# shutdown
+# dot1q native vlan 20
+# !
+# interface GigabitEthernet0/0/0/4
+# description Test description
+# dot1q native vlan 10
+# l2transport
+# l2protocol stp tunnel
+# !
+# !
+# interface GigabitEthernet0/0/0/3.900 l2transport
+# dot1q vlan 20 40
+# !
+
+- name: Replaces device configuration of listed interfaces with provided configuration
+ iosxr_l2_interfaces:
+ config:
+ - name: GigabitEthernet0/0/0/4
+ native_vlan: 40
+ l2transport: True
+ l2protocol:
+ - stp: forward
+ - name: GigabitEthernet0/0/0/3.900
+ q_vlan:
+ - 20
+ - any
+ state: replaced
+
+# After state:
+# -------------
+#
+# viosxr#show running-config interface
+# interface GigabitEthernet0/0/0/3
+# description Ansible Network
+# vrf custB
+# ipv4 address 10.10.0.2 255.255.255.0
+# duplex half
+# shutdown
+# dot1q native vlan 20
+# !
+# interface GigabitEthernet0/0/0/4
+# description Test description
+# dot1q native vlan 40
+# l2transport
+# l2protocol stp forward
+# !
+# !
+# interface GigabitEthernet0/0/0/3.900 l2transport
+# dot1q vlan 20 any
+# !
+
+# Using overridden
+#
+# Before state:
+# -------------
+#
+# viosxr#show running-config interface
+# interface GigabitEthernet0/0/0/3
+# description Ansible Network
+# vrf custB
+# ipv4 address 10.10.0.2 255.255.255.0
+# duplex half
+# shutdown
+# dot1q native vlan 20
+# !
+# interface GigabitEthernet0/0/0/4
+# description Test description
+# dot1q native vlan 10
+# l2transport
+# l2protocol stp tunnel
+# !
+# !
+# interface GigabitEthernet0/0/0/3.900 l2transport
+# dot1q vlan 20 40
+# !
+
+- name: Override device configuration of all interfaces with provided configuration
+ iosxr_l2_interfaces:
+ config:
+ - name: GigabitEthernet0/0/0/4
+ native_vlan: 40
+ l2transport: True
+ l2protocol:
+ - stp: forward
+ - name: GigabitEthernet0/0/0/3.900
+ q_vlan:
+ - 20
+ - any
+ state: overridden
+
+# After state:
+# -------------
+#
+# viosxr#show running-config interface
+# interface GigabitEthernet0/0/0/3
+# description Ansible Network
+# vrf custB
+# ipv4 address 10.10.0.2 255.255.255.0
+# duplex half
+# shutdown
+# !
+# interface GigabitEthernet0/0/0/4
+# description Test description
+# dot1q native vlan 40
+# l2transport
+# l2protocol stp forward
+# !
+# !
+# interface GigabitEthernet0/0/0/3.900
+# dot1q vlan 20 any
+# !
+
+# Using deleted
+#
+# Before state:
+# -------------
+#
+# viosxr#show running-config interface
+# interface GigabitEthernet0/0/0/3
+# description Ansible Network
+# vrf custB
+# ipv4 address 10.10.0.2 255.255.255.0
+# duplex half
+# shutdown
+# dot1q native vlan 20
+# !
+# interface GigabitEthernet0/0/0/4
+# description Test description
+# dot1q native vlan 10
+# l2transport
+# l2protocol stp tunnel
+# !
+# !
+
+- name: Delete L2 attributes of given interfaces (Note: This won't delete the interface itself)
+ iosxr_l2_interfaces:
+ config:
+ - name: GigabitEthernet0/0/0/4
+ state: deleted
+
+# After state:
+# ------------
+#
+# viosxr#show running-config interface
+# interface GigabitEthernet0/0/0/3
+# description Ansible Network
+# vrf custB
+# ipv4 address 10.10.0.2 255.255.255.0
+# duplex half
+# shutdown
+# dot1q native vlan 20
+# !
+# interface GigabitEthernet0/0/0/4
+# description Test description
+# !
+
+# Using Deleted without any config passed
+# "(NOTE: This will delete all of configured resource module attributes from each configured interface)"
+#
+# Before state:
+# -------------
+#
+# viosxr#show running-config interface
+# interface GigabitEthernet0/0/0/3
+# description Ansible Network
+# vrf custB
+# ipv4 address 10.10.0.2 255.255.255.0
+# duplex half
+# shutdown
+# dot1q native vlan 20
+# !
+# interface GigabitEthernet0/0/0/4
+# description Test description
+# dot1q native vlan 10
+# l2transport
+# l2protocol stp tunnel
+# !
+# !
+
+- name: Delete L2 attributes of all interfaces (Note: This won't delete the interface itself)
+ iosxr_l2_interfaces:
+ state: deleted
+
+# After state:
+# ------------
+#
+# viosxr#show running-config interface
+# interface GigabitEthernet0/0/0/3
+# description Ansible Network
+# vrf custB
+# ipv4 address 10.10.0.2 255.255.255.0
+# duplex half
+# shutdown
+# !
+# interface GigabitEthernet0/0/0/4
+# description Test description
+# !
+"""
+
+RETURN = """
+before:
+ description: The configuration prior to the model invocation
+ returned: always
+ type: list
+ sample: The configuration returned will alwys 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 alwys 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/0/0/2', 'command 2', 'command 3']
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.network.iosxr.argspec.l2_interfaces.l2_interfaces import L2_InterfacesArgs
+from ansible.module_utils.network.iosxr.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()
diff --git a/module_utils/__init__.py b/module_utils/__init__.py
new file mode 100644
index 0000000..e69de29
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/iosxr/__init__.py b/module_utils/network/iosxr/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/module_utils/network/iosxr/argspec/__init__.py b/module_utils/network/iosxr/argspec/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/module_utils/network/iosxr/argspec/__init__.py
@@ -0,0 +1 @@
+
diff --git a/module_utils/network/iosxr/argspec/facts/__init__.py b/module_utils/network/iosxr/argspec/facts/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/module_utils/network/iosxr/argspec/facts/facts.py b/module_utils/network/iosxr/argspec/facts/facts.py
new file mode 100644
index 0000000..cb3bb41
--- /dev/null
+++ b/module_utils/network/iosxr/argspec/facts/facts.py
@@ -0,0 +1,30 @@
+#
+# -*- 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 iosxr facts module.
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class FactsArgs(object): # pylint: disable=R0903
+ """ The arg spec for the iosxr facts module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ choices = [
+ 'all',
+ 'l2_interfaces',
+ '!l2_interfaces'
+ ]
+
+ argument_spec = {
+ 'gather_subset': dict(default=['!config'], type='list'),
+ 'gather_network_resources': dict(choices=choices, type='list'),
+ }
diff --git a/module_utils/network/iosxr/argspec/l2_interfaces/__init__.py b/module_utils/network/iosxr/argspec/l2_interfaces/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/module_utils/network/iosxr/argspec/l2_interfaces/l2_interfaces.py b/module_utils/network/iosxr/argspec/l2_interfaces/l2_interfaces.py
new file mode 100644
index 0000000..e3535cb
--- /dev/null
+++ b/module_utils/network/iosxr/argspec/l2_interfaces/l2_interfaces.py
@@ -0,0 +1,57 @@
+#
+# -*- 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},
+ 'native_vlan': {'type': 'int'},
+ 'l2transport': {'type': 'bool'},
+ 'l2protocol': {'element': 'dict',
+ 'type': 'list',
+ 'options':{'cdp': {'type': 'str',
+ 'choices': ['drop','forward', 'tunnel']},
+ 'pvst': {'type': 'str',
+ 'choices': ['drop','forward', 'tunnel']},
+ 'stp': {'type': 'str',
+ 'choices': ['drop', 'forward', 'tunnel']},
+ 'vtp': {'type': 'str',
+ 'choices': ['drop', 'forward', 'tunnel']},
+ }},
+ 'q_vlan': {'type': 'list'},
+ 'propagate': {'type': 'bool'}},
+ 'type': 'list'},
+ 'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'}}
diff --git a/module_utils/network/iosxr/config/__init__.py b/module_utils/network/iosxr/config/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/module_utils/network/iosxr/config/base.py b/module_utils/network/iosxr/config/base.py
new file mode 100644
index 0000000..7ef97ea
--- /dev/null
+++ b/module_utils/network/iosxr/config/base.py
@@ -0,0 +1,24 @@
+#!/usr/bin/python
+# -*- 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 base class for all iosxr resource modules
+"""
+
+from ansible.module_utils.connection import Connection
+
+class ConfigBase(object): #pylint: disable=R0205,R0903
+ """ The base class for all iosxr resource modules
+ """
+ _connection = None
+
+ def __init__(self, module):
+ self._module = module
+ self._connection = self._get_connection()
+
+ def _get_connection(self):
+ if self._connection:
+ return self._connection
+ self._connection = Connection(self._module._socket_path) #pylint: disable=W0212
+ return self._connection
diff --git a/module_utils/network/iosxr/config/l2_interfaces/__init__.py b/module_utils/network/iosxr/config/l2_interfaces/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/module_utils/network/iosxr/config/l2_interfaces/l2_interfaces.py b/module_utils/network/iosxr/config/l2_interfaces/l2_interfaces.py
new file mode 100644
index 0000000..9931fbc
--- /dev/null
+++ b/module_utils/network/iosxr/config/l2_interfaces/l2_interfaces.py
@@ -0,0 +1,308 @@
+# -*- 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 iosxr_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.iosxr.facts.facts import Facts
+from ansible.module_utils.network.iosxr.utils.utils import get_interface_type, dict_diff
+from ansible.module_utils.network.iosxr.utils.utils import remove_command_from_config_list, add_command_to_config_list
+from ansible.module_utils.network.iosxr.utils.utils import filter_dict_having_none_value, remove_duplicate_interface
+
+
+class L2_Interfaces(ConfigBase):
+ """
+ The iosxr_interfaces class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'l2_interfaces',
+ ]
+
+ def get_l2_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)
+ l2_interfaces_facts = facts['ansible_network_resources'].get('l2_interfaces')
+
+ if not l2_interfaces_facts:
+ return []
+ return l2_interfaces_facts
+
+ def execute_module(self):
+ """ Execute the module
+ :rtype: A dictionary
+ :returns: The result from module execution
+ """
+ result = {'changed': False}
+ commands = list()
+ warnings = list()
+
+ existing_l2_interfaces_facts = self.get_l2_interfaces_facts()
+ commands.extend(self.set_config(existing_l2_interfaces_facts))
+ if commands:
+ if not self._module.check_mode:
+ self._connection.edit_config(commands)
+ result['changed'] = True
+ result['commands'] = commands
+
+ changed_l2_interfaces_facts = self.get_l2_interfaces_facts()
+
+ result['before'] = existing_l2_interfaces_facts
+ if result['changed']:
+ result['after'] = changed_l2_interfaces_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_l2_interfaces_facts):
+ """ Collect the configuration from the args passed to the module,
+ collect the current configuration (as a dict from facts)
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ want = self._module.params['config']
+ have = existing_l2_interfaces_facts
+ resp = self.set_state(want, have)
+ return to_list(resp)
+
+ def set_state(self, want, have):
+ """ Select the appropriate function based on the state provided
+ :param want: the desired configuration as a dictionary
+ :param have: the current configuration as a dictionary
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ 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
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+
+ for interface in want:
+ for each in have:
+ if each['name'] == interface['name']:
+ break
+ else:
+ commands.extend(self._set_config(interface, {}, module))
+ continue
+ have_dict = filter_dict_having_none_value(interface, each)
+ new_want = dict()
+ commands.extend(self._clear_config(new_want, 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
+ :rtype: A list
+ :returns: the commands necessary to migrate the current configuration
+ to the desired configuration
+ """
+ commands = []
+ not_in_have = {()}
+ in_have = {()}
+
+ for each in have:
+ for interface in want:
+ if each['name'] == interface['name']:
+ in_have.add(interface['name'])
+ break
+ elif interface['name'] != each['name']:
+ not_in_have.add(interface['name'])
+ 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'])
+ commands.extend(self._clear_config(interface, each))
+ continue
+ have_dict = filter_dict_having_none_value(interface, each)
+ new_want = dict()
+ commands.extend(self._clear_config(new_want, have_dict))
+ commands.extend(self._set_config(interface, each, module))
+ # Add the want interface that's not already configured in have interface
+ for each in (not_in_have - in_have):
+ for every in want:
+ interface = 'interface {0}'.format(every['name'])
+ if each and interface not in commands:
+ commands.extend(self._set_config(every, {}, 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
+ :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
+ elif interface['name'] in each['name']:
+ break
+ else:
+ commands.extend(self._set_config(interface, {}, module))
+ continue
+ commands.extend(self._set_config(interface, each, module))
+
+ return commands
+
+ def _state_deleted(self, want, have):
+ """ The command generator when state is deleted
+ :rtype: A list
+ :returns: the commands necessary to remove the current configuration
+ of the provided objects
+ """
+ commands = []
+
+ if want:
+ for interface in want:
+ for each in have:
+ if each['name'] == interface['name']:
+ break
+ elif interface['name'] in each['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 _set_config(self, want, have, module):
+ # Set the interface config based on the want and have config
+ commands = []
+ interface = 'interface ' + want['name']
+ l2_protocol_bool = False
+ # Get the diff b/w want and have
+ want_dict = dict_diff(want)
+ have_dict = dict_diff(have)
+ diff = want_dict - have_dict
+ if diff:
+ # For merging with already configured l2protocol
+ if have.get('l2protocol') and len(have.get('l2protocol')) > 1:
+ l2_protocol_diff = []
+ for each in want.get('l2protocol'):
+ for every in have.get('l2protocol'):
+ if every == each:
+ break
+ if each not in have.get('l2protocol'):
+ l2_protocol_diff.append(each)
+ l2_protocol_bool = True
+ l2protocol = tuple(l2_protocol_diff)
+ else:
+ l2protocol = {}
+
+ diff = dict(diff)
+ wants_native = diff.get('native_vlan')
+ l2transport = diff.get('l2transport')
+ q_vlan = diff.get('q_vlan')
+ propagate = diff.get('propagate')
+ if l2_protocol_bool is False:
+ l2protocol = diff.get('l2protocol')
+
+ if wants_native and 'preconfigure' not in interface:
+ cmd = 'dot1q native vlan {0}'.format(wants_native)
+ add_command_to_config_list(interface, cmd, commands)
+ elif wants_native and 'preconfigure' in interface:
+ module.fail_json(msg='Native Vlan cannot be configured over Sub-Interface: {0}'.format(interface))
+ if l2transport or l2protocol:
+ for each in l2protocol:
+ if isinstance(each, dict):
+ cmd = 'l2transport l2protocol {0} {1}'.format(each.keys()[0], each.values()[0])
+ else:
+ cmd = 'l2transport l2protocol {0} {1}'.format(each[0], each[1])
+ add_command_to_config_list(interface, cmd, commands)
+ if propagate and not have.get('propagate'):
+ cmd = 'l2transport propagate remote-status'
+ add_command_to_config_list(interface, cmd, commands)
+ elif want.get('l2transport') is False and (want.get('l2protocol') or want.get('propagate')):
+ module.fail_json(msg='L2transport L2protocol or Propagate can only be configured when '
+ 'L2transport set to True!')
+
+ if q_vlan and 'preconfigure' in interface:
+ q_vlans = (" ".join(map(str, want.get('q_vlan'))))
+ if q_vlans != have.get('q_vlan'):
+ if 'any' in q_vlans and 'l2transport' in interface:
+ cmd = 'dot1q vlan {}'.format(q_vlans)
+ add_command_to_config_list(interface, cmd, commands)
+ else:
+ cmd = 'dot1q vlan {}'.format(q_vlans)
+ add_command_to_config_list(interface, cmd, commands)
+ elif q_vlan and 'preconfigure' not in interface:
+ module.fail_json(msg='Option q_vlan is 802.1Q VLAN configuration which is only supported on '
+ 'Sub-Interface!')
+
+ 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('native_vlan'):
+ remove_command_from_config_list(interface, 'dot1q native vlan', commands)
+
+ if have.get('q_vlan') and 'l2transport' in interface:
+ remove_command_from_config_list(interface, 'dot1q vlan', commands)
+ if have.get('q_vlan') and 'preconfigure' in interface and 'l2transport' not in interface:
+ remove_command_from_config_list(interface, 'encapsulation dot1q', commands)
+
+ if have.get('l2protocol') and (want.get('l2protocol') is None or want.get('propagate') is None):
+ if 'no l2transport' not in commands:
+ remove_command_from_config_list(interface, 'l2transport', commands)
+ elif have.get('l2transport') and have.get('l2transport') != want.get('l2transport'):
+ if 'no l2transport' not in commands:
+ remove_command_from_config_list(interface, 'l2transport', commands)
+
+ return commands
diff --git a/module_utils/network/iosxr/config/resource/__init__.py b/module_utils/network/iosxr/config/resource/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/module_utils/network/iosxr/config/resource/resource.py b/module_utils/network/iosxr/config/resource/resource.py
new file mode 100644
index 0000000..e69de29
diff --git a/module_utils/network/iosxr/facts/__init__.py b/module_utils/network/iosxr/facts/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/module_utils/network/iosxr/facts/base.py b/module_utils/network/iosxr/facts/base.py
new file mode 100644
index 0000000..8c1a03d
--- /dev/null
+++ b/module_utils/network/iosxr/facts/base.py
@@ -0,0 +1,110 @@
+#!/usr/bin/python
+# -*- 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 iosxr facts base class
+this contains methods common to all facts subsets
+"""
+
+import re
+from copy import deepcopy
+from ansible.module_utils.six import iteritems
+
+
+class FactsBase(object): #pylint: disable=R0205
+ """
+ The iosxr facts base class
+ """
+ generated_spec = {}
+ ansible_facts = {'ansible_network_resources': {}}
+
+ def __init__(self, argspec, subspec=None, options=None):
+ spec = deepcopy(argspec)
+ if subspec:
+ if options:
+ facts_argument_spec = spec[subspec][options]
+ else:
+ facts_argument_spec = spec[subspec]
+ else:
+ facts_argument_spec = spec
+
+ self.generated_spec = self.generate_dict(facts_argument_spec)
+
+ @staticmethod
+ def generate_dict(spec):
+ """
+ Generate dictionary which is in sync with argspec
+
+ :param spec: A dictionary which the argspec of module
+ :rtype: A dictionary
+ :returns: A dictionary in sync with argspec with default value
+ """
+ obj = {}
+ if not spec:
+ return obj
+
+ for key, val in iteritems(spec):
+ if 'default' in val:
+ dct = {key: val['default']}
+ else:
+ dct = {key: None}
+ obj.update(dct)
+
+ return obj
+
+ @staticmethod
+ def parse_conf_arg(cfg, arg):
+ """
+ Parse config based on argument
+
+ :param cfg: A text string which is a line of configuration.
+ :param arg: A text string which is to be matched.
+ :rtype: A text string
+ :returns: A text string if match is found
+ """
+ match = re.search(r'%s (.+)(\n|$)' % arg, cfg, re.M)
+ if match:
+ result = match.group(1).strip()
+ else:
+ result = None
+ return result
+
+ @staticmethod
+ def parse_conf_cmd_arg(cfg, cmd, res1, res2=None):
+ """
+ Parse config based on command
+
+ :param cfg: A text string which is a line of configuration.
+ :param cmd: A text string which is the command to be matched
+ :param res1: A text string to be returned if the command is present
+ :param res2: A text string to be returned if the negate command is present
+ :rtype: A text string
+ :returns: A text string if match is found
+ """
+ match = re.search(r'\n\s+%s(\n|$)' % cmd, cfg)
+ if match:
+ return res1
+ if res2 is not None:
+ match = re.search(r'\n\s+no %s(\n|$)' % cmd, cfg)
+ if match:
+ return res2
+ return None
+
+ @staticmethod
+ def generate_final_config(cfg_dict):
+ """
+ Generate final config dictionary
+
+ :param cfg_dict: A dictionary parsed in the facts system
+ :rtype: A dictionary
+ :returns: A dictionary by eliminating keys that have null values
+ """
+ final_cfg = {}
+ if not cfg_dict:
+ return final_cfg
+
+ for key, val in iteritems(cfg_dict):
+ if val:
+ final_cfg.update({key:val})
+ return final_cfg
\ No newline at end of file
diff --git a/module_utils/network/iosxr/facts/facts.py b/module_utils/network/iosxr/facts/facts.py
new file mode 100644
index 0000000..60a1de6
--- /dev/null
+++ b/module_utils/network/iosxr/facts/facts.py
@@ -0,0 +1,62 @@
+#
+# -*- 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 facts class for iosxr
+this file validates each subset of facts and selectively
+calls the appropriate facts gathering function
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+from ansible.module_utils.network.iosxr.argspec.facts.facts import FactsArgs
+from ansible.module_utils.network.common.facts.facts import FactsBase
+from ansible.module_utils.network.iosxr.facts.lacp.lacp import LacpFacts
+from ansible.module_utils.network.iosxr.facts.l2_interfaces.l2_interfaces import L2_InterfacesFacts
+from ansible.module_utils.network.iosxr.facts.legacy.\
+ base import Default, Hardware, Interfaces, Config
+
+
+FACT_LEGACY_SUBSETS = dict(
+ default=Default,
+ hardware=Hardware,
+ interfaces=Interfaces,
+ config=Config,
+)
+FACT_RESOURCE_SUBSETS = dict(
+ lacp=LacpFacts,
+ l2_interfaces=L2_InterfacesFacts,
+)
+
+
+class Facts(FactsBase):
+ """ The fact class for iosxr
+ """
+
+ VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys())
+ VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys())
+
+ def __init__(self, module):
+ super(Facts, self).__init__(module)
+
+ def get_facts(self, legacy_facts_type=None, resource_facts_type=None, data=None):
+ """ Collect the facts for iosxr
+
+ :param legacy_facts_type: List of legacy facts types
+ :param resource_facts_type: List of resource fact types
+ :param data: previously collected conf
+ :rtype: dict
+ :return: the facts gathered
+ """
+ netres_choices = FactsArgs.argument_spec['gather_network_resources'].get('choices', [])
+ if self.VALID_RESOURCE_SUBSETS:
+ self.get_network_resources_facts(netres_choices, FACT_RESOURCE_SUBSETS, resource_facts_type, data)
+
+ if self.VALID_LEGACY_GATHER_SUBSETS:
+ self.get_network_legacy_facts(FACT_LEGACY_SUBSETS, legacy_facts_type)
+
+ return self.ansible_facts, self._warnings
diff --git a/module_utils/network/iosxr/facts/l2_interfaces/__init__.py b/module_utils/network/iosxr/facts/l2_interfaces/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/module_utils/network/iosxr/facts/l2_interfaces/l2_interfaces.py b/module_utils/network/iosxr/facts/l2_interfaces/l2_interfaces.py
new file mode 100644
index 0000000..30b0e5e
--- /dev/null
+++ b/module_utils/network/iosxr/facts/l2_interfaces/l2_interfaces.py
@@ -0,0 +1,129 @@
+#
+# -*- 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 iosxr l2_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.iosxr.utils.utils import get_interface_type, normalize_interface
+from ansible.module_utils.network.iosxr.argspec.l2_interfaces.l2_interfaces import L2_InterfacesArgs
+
+
+class L2_InterfacesFacts(object):
+ """ The iosxr 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 l2_interfaces
+ :param module: the module instance
+ :param connection: the device connection
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ objs = []
+ if not data:
+ data = connection.get('show running-config 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)
+ config['name'] = normalize_interface(intf)
+
+ if match.group(1).lower() == "preconfigure":
+ match = re.search(r'^(\S+).*', conf)
+ if match:
+ config['name'] = match.group()
+ if get_interface_type(intf) == 'unknown':
+ return {}
+
+ # populate the facts from the configuration
+ native_vlan = re.search(r"dot1q native vlan (\d+)", conf)
+ if native_vlan:
+ config["native_vlan"] = int(native_vlan.group(1))
+
+ if 'l2transport' in config['name']:
+ dot1q = utils.parse_conf_arg(conf, 'dot1q vlan')
+ config['q_vlan'] = []
+ if dot1q:
+ config['q_vlan'].append(int(dot1q.split(' ')[0]))
+ config['q_vlan'].append(dot1q.split(' ')[1])
+ if 'preconfigure' in config['name'] and 'l2transport' not in config['name']:
+ dot1q = utils.parse_conf_arg(conf, 'encapsulation dot1q')
+ config['q_vlan'] = []
+ if dot1q:
+ config['q_vlan'].append(int(dot1q.split(' ')[0]))
+ config['q_vlan'].append(int(dot1q.split(' ')[2]))
+
+ if utils.parse_conf_cmd_arg(conf, 'l2transport', True):
+ config['l2transport'] = True
+ if utils.parse_conf_arg(conf, 'propagate'):
+ config['propagate'] = True
+ config['l2protocol'] = []
+
+ cdp = utils.parse_conf_arg(conf, 'l2protocol cdp')
+ pvst = utils.parse_conf_arg(conf, 'l2protocol pvst')
+ stp = utils.parse_conf_arg(conf, 'l2protocol stp')
+ vtp = utils.parse_conf_arg(conf, 'l2protocol vtp')
+ if cdp:
+ config['l2protocol'].append({'cdp': cdp})
+ if pvst:
+ config['l2protocol'].append({'pvst': pvst})
+ if stp:
+ config['l2protocol'].append({'stp': stp})
+ if vtp:
+ config['l2protocol'].append({'vtp': vtp})
+
+ return utils.remove_empties(config)
diff --git a/module_utils/network/iosxr/facts/resource/__init__.py b/module_utils/network/iosxr/facts/resource/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/module_utils/network/iosxr/facts/resource/resource.py b/module_utils/network/iosxr/facts/resource/resource.py
new file mode 100644
index 0000000..1d5daca
--- /dev/null
+++ b/module_utils/network/iosxr/facts/resource/resource.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python
+# -*- 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 iosxr_l2_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 copy import deepcopy
+from ansible.module_utils.iosxr.facts.base import FactsBase
+
+class Facts(FactsBase):
+ """ The iosxr l2 fact class
+ """
+
+ def populate_facts(self, module, connection, data=None):
+ """ Populate the facts for l2_interfaces
+
+ :param module: the module instance
+ :param connection: the device connection
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ if module: #just for linting purposes
+ pass
+ if connection: #just for linting purposes
+ pass
+
+ if not data:
+ data = "foo" # connection.get('show running-config | section ^interface')
+
+ # operate on a collection of resource x
+ config = [data] # data.split('interface ')
+ objs = []
+ 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'] = objs
+ self.ansible_facts['ansible_network_resources'].update(facts)
+ return self.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
+ """
+ if conf:
+ pass
+ config = deepcopy(spec)
+ # populate the facts from the configuration
+ config = {"some": "value"}
+ return self.generate_final_config(config)
\ No newline at end of file
diff --git a/module_utils/network/iosxr/utils/__init__.py b/module_utils/network/iosxr/utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/module_utils/network/iosxr/utils/utils.py b/module_utils/network/iosxr/utils/utils.py
new file mode 100644
index 0000000..082a5d7
--- /dev/null
+++ b/module_utils/network/iosxr/utils/utils.py
@@ -0,0 +1,165 @@
+# -*- 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
+
+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_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:
+ if type(v) is list:
+ if type(v[0]) is dict:
+ li = []
+ for each in v:
+ li.extend(tuple(each.items()))
+ v = tuple(li)
+ else:
+ 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_dict['name'] = want.get('name')
+ for k, v in iteritems(want):
+ if k == 'l2protocol':
+ if want[k] != have.get('l2protocol') and have.get('l2protocol'):
+ test_dict.update({k: v})
+ 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 flatten_dict(x):
+ result = {}
+ if not isinstance(x, dict):
+ return result
+
+ for key, value in iteritems(x):
+ if isinstance(value, dict):
+ result.update(flatten_dict(value))
+ else:
+ result[key] = value
+
+ return result
+
+
+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('fa'):
+ if_type = 'FastEthernet'
+ elif name.lower().startswith('fo'):
+ if_type = 'FortyGigE'
+ 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('FA'):
+ return 'FastEthernet'
+ elif interface.upper().startswith('FO'):
+ return 'FortyGigE'
+ 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'
+ elif interface.upper().startswith('PRE'):
+ return 'preconfigure'
+ else:
+ return 'unknown'