NX-OS ACL interfaces module (#67505)

* Rebase

* Completed integration tests

* Added unit tests

* Added warning detection

* Updated tests

* Completed tests

* Linting

Linting II

YAML Lint

Linting

* Updated review changes

* Updated examples, fixed reviews

* Added failure condition

* Resolved merge conflict
This commit is contained in:
Adharsh Srivats R 2020-03-01 23:22:32 -05:00 committed by GitHub
parent 42eba3ce25
commit f3ddf1bc95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 1849 additions and 14 deletions

View file

@ -0,0 +1,91 @@
#
# -*- 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 nxos_acl_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Acl_interfacesArgs(object): # pylint: disable=R0903
"""The arg spec for the nxos_acl_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
'config': {
'elements': 'dict',
'options': {
'access_groups': {
'elements': 'dict',
'options': {
'acls': {
'elements': 'dict',
'options': {
'direction': {
'choices': ['in', 'out'],
'required': True,
'type': 'str'
},
'name': {
'required': True,
'type': 'str'
},
'port': {
'type': 'bool'
}
},
'type': 'list'
},
'afi': {
'choices': ['ipv4', 'ipv6'],
'required': True,
'type': 'str'
}
},
'type': 'list'
},
'name': {
'required': True,
'type': 'str'
}
},
'type': 'list'
},
'running_config': {
'type': 'str'
},
'state': {
'choices': [
'deleted', 'gathered', 'merged', 'overridden', 'rendered',
'replaced', 'parsed'
],
'default':
'merged',
'type':
'str'
}
} # pylint: disable=C0301

View file

@ -0,0 +1,303 @@
#
# -*- 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 nxos_acl_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, remove_empties, dict_diff
from ansible.module_utils.network.nxos.facts.facts import Facts
from ansible.module_utils.network.nxos.utils.utils import flatten_dict, search_obj_in_list, get_interface_type, normalize_interface
class Acl_interfaces(ConfigBase):
"""
The nxos_acl_interfaces class
"""
gather_subset = [
'!all',
'!min',
]
gather_network_resources = [
'acl_interfaces',
]
def __init__(self, module):
super(Acl_interfaces, self).__init__(module)
def get_acl_interfaces_facts(self, data=None):
""" 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, data=data)
acl_interfaces_facts = facts['ansible_network_resources'].get(
'acl_interfaces')
if not acl_interfaces_facts:
return []
return acl_interfaces_facts
def edit_config(self, commands):
"""Wrapper method for `_connection.edit_config()`
This exists solely to allow the unit test framework to mock device connection calls.
"""
return self._connection.edit_config(commands)
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {'changed': False}
warnings = list()
commands = list()
state = self._module.params['state']
action_states = ['merged', 'replaced', 'deleted', 'overridden']
if state == 'gathered':
result['gathered'] = self.get_acl_interfaces_facts()
elif state == 'rendered':
result['rendered'] = self.set_config({})
# no need to fetch facts for rendered
elif state == 'parsed':
result['parsed'] = self.set_config({})
# no need to fetch facts for parsed
else:
existing_acl_interfaces_facts = self.get_acl_interfaces_facts()
commands.extend(self.set_config(existing_acl_interfaces_facts))
if commands and state in action_states:
if not self._module.check_mode:
self._connection.edit_config(commands)
result['changed'] = True
result['before'] = existing_acl_interfaces_facts
result['commands'] = commands
changed_acl_interfaces_facts = self.get_acl_interfaces_facts()
if result['changed']:
result['after'] = changed_acl_interfaces_facts
result['warnings'] = warnings
return result
def set_config(self, existing_acl_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
"""
config = self._module.params['config']
want = []
if config:
for w in config:
if get_interface_type(w['name']) == 'loopback':
self._module.fail_json(
msg='This module works with ethernet, management or port-channe')
w.update({'name': normalize_interface(w['name'])})
want.append(remove_empties(w))
have = existing_acl_interfaces_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, want, have):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
state = self._module.params['state']
commands = []
if state == 'overridden':
commands = (self._state_overridden(want, have))
elif state == 'deleted':
commands = (self._state_deleted(want, have))
elif state == 'rendered':
commands = self._state_rendered(want)
elif state == 'parsed':
want = self._module.params['running_config']
commands = self._state_parsed(want)
else:
for w in want:
if state == 'merged':
commands.extend(self._state_merged(w, have))
elif state == 'replaced':
commands.extend(self._state_replaced(w, have))
return commands
def _state_parsed(self, want):
return self.get_acl_interfaces_facts(want)
def _state_rendered(self, want):
commands = []
for w in want:
commands.extend(self.set_commands(w, {}))
return commands
def _state_replaced(self, want, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
new_commands = []
del_dict = {'name': want['name'], 'access_groups': []}
obj_in_have = search_obj_in_list(want['name'], have, 'name')
if obj_in_have != want:
commands = []
if obj_in_have and 'access_groups' in obj_in_have.keys():
for ag in obj_in_have['access_groups']:
want_afi = []
if want.get('access_groups'):
want_afi = search_obj_in_list(
ag['afi'], want['access_groups'], 'afi')
if not want_afi:
# whatever in have is not in want
del_dict['access_groups'].append(ag)
else:
del_acl = []
for acl in ag['acls']:
if want_afi.get('acls'):
if acl not in want_afi['acls']:
del_acl.append(acl)
else:
del_acl.append(acl)
afi = want_afi['afi']
del_dict['access_groups'].append(
{'afi': afi, 'acls': del_acl})
commands.extend(self._state_deleted([del_dict], have))
commands.extend(self._state_merged(want, have))
new_commands.append(commands[0])
commands = [commands[i]
for i in range(1, len(commands)) if commands[i] != commands[0]]
new_commands.extend(commands)
return new_commands
def _state_overridden(self, want, have):
""" The command generator when state is overridden
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
want_intf = [w['name'] for w in want]
for h in have:
if h['name'] not in want_intf:
commands.extend(self._state_deleted([h], have))
for w in want:
commands.extend(self._state_replaced(w, have))
return commands
def _state_merged(self, want, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
return self.set_commands(want, have)
def set_commands(self, want, have, deleted=False):
commands = []
have_name = search_obj_in_list(want['name'], have, 'name')
if have_name and have_name.get('access_groups'):
if want.get('access_groups'):
for w_afi in want['access_groups']:
ip = 'ipv6'
if w_afi['afi'] == 'ipv4':
ip = 'ip'
have_afi = search_obj_in_list(
w_afi['afi'], have_name['access_groups'], 'afi')
if have_afi:
new_acls = []
if deleted:
if w_afi.get('acls') and have_afi.get('acls'):
new_acls = [
acl for acl in w_afi.get('acls') if acl in have_afi.get('acls')]
elif 'acls' not in w_afi.keys():
new_acls = have_afi.get('acls')
else:
if w_afi.get('acls'):
new_acls = [
acl for acl in w_afi['acls'] if acl not in have_afi['acls']]
commands.extend(self.process_acl(
new_acls, ip, deleted))
else:
if not deleted:
if w_afi.get('acls'):
commands.extend(
self.process_acl(w_afi['acls'], ip))
else:
# only name is given to delete
if deleted and 'access_groups' in have_name.keys():
commands.extend(self.process_access_group(have_name, True))
else:
if not deleted: # and 'access_groups' in have_name.keys():
commands.extend(self.process_access_group(want))
if len(commands) > 0:
commands.insert(0, 'interface ' + want['name'])
return commands
def process_access_group(self, item, deleted=False):
commands = []
for ag in item['access_groups']:
ip = 'ipv6'
if ag['afi'] == 'ipv4':
ip = 'ip'
if ag.get('acls'):
commands.extend(self.process_acl(
ag['acls'], ip, deleted))
return commands
def process_acl(self, acls, ip, deleted=False):
commands = []
no = ''
if deleted:
no = 'no '
for acl in acls:
port = ''
if acl.get('port'):
port = ' port'
ag = ' access-group '
if ip == 'ipv6':
ag = ' traffic-filter '
commands.append(no + ip + port + ag +
acl['name'] + ' ' + acl['direction'])
return commands
def _state_deleted(self, main_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 main_want:
for want in main_want:
commands.extend(self.set_commands(want, have, deleted=True))
else:
for h in have:
commands.extend(self.set_commands(h, have, deleted=True))
return commands

View file

@ -0,0 +1,119 @@
#
# -*- 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 nxos acl_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
import re
from copy import deepcopy
from ansible.module_utils.network.common import utils
from ansible.module_utils.network.nxos.argspec.acl_interfaces.acl_interfaces import Acl_interfacesArgs
from ansible.module_utils.network.nxos.utils.utils import normalize_interface
class Acl_interfacesFacts(object):
""" The nxos acl_interfaces fact class
"""
def __init__(self, module, subspec='config', options='options'):
self._module = module
self.argument_spec = Acl_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 get_device_data(self, connection):
return connection.get('show running-config | section interface')
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for acl_interfaces
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
if not data:
data = self.get_device_data(connection)
data = data.split('interface')
resources = []
for i in range(len(data)):
intf = data[i].split('\n')
for l in range(1, len(intf)):
if not re.search('ip(v6)?( port)? (access-group|traffic-filter)', intf[l]):
intf[l] = ''
intf = list(filter(None, intf))
resources.append(intf)
objs = []
for resource in resources:
if resource:
obj = self.render_config(self.generated_spec, resource)
if obj:
objs.append(obj)
ansible_facts['ansible_network_resources'].pop('acl_interfaces', None)
facts = {}
if objs:
params = utils.validate_config(
self.argument_spec, {'config': objs})
params = utils.remove_empties(params)
facts['acl_interfaces'] = params['config']
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)
name = conf[0].strip()
config['name'] = normalize_interface(name)
config['access_groups'] = []
v4 = {'afi': 'ipv4', 'acls': []}
v6 = {'afi': 'ipv6', 'acls': []}
for c in conf[1:]:
if c:
acl4 = re.search(r'ip( port)? access-group (\w*) (\w*)', c)
acl6 = re.search(r'ipv6( port)? traffic-filter (\w*) (\w*)', c)
if acl4:
acl = {'name': acl4.group(2).strip(
), 'direction': acl4.group(3).strip()}
if acl4.group(1):
acl.update({'port': True})
v4['acls'].append(acl)
elif acl6:
acl = {'name': acl6.group(2), 'direction': acl6.group(3)}
if acl6.group(1):
acl.update({'port': True})
v6['acls'].append(acl)
if len(v4['acls']) > 0:
config['access_groups'].append(v4)
if len(v6['acls']) > 0:
config['access_groups'].append(v6)
return utils.remove_empties(config)

View file

@ -23,6 +23,7 @@ from ansible.module_utils.network.nxos.facts.vlans.vlans import VlansFacts
from ansible.module_utils.network.nxos.facts.lacp_interfaces.lacp_interfaces import Lacp_interfacesFacts from ansible.module_utils.network.nxos.facts.lacp_interfaces.lacp_interfaces import Lacp_interfacesFacts
from ansible.module_utils.network.nxos.facts.lldp_global.lldp_global import Lldp_globalFacts from ansible.module_utils.network.nxos.facts.lldp_global.lldp_global import Lldp_globalFacts
from ansible.module_utils.network.nxos.facts.lldp_interfaces.lldp_interfaces import Lldp_interfacesFacts from ansible.module_utils.network.nxos.facts.lldp_interfaces.lldp_interfaces import Lldp_interfacesFacts
from ansible.module_utils.network.nxos.facts.acl_interfaces.acl_interfaces import Acl_interfacesFacts
FACT_LEGACY_SUBSETS = dict( FACT_LEGACY_SUBSETS = dict(
@ -46,6 +47,7 @@ FACT_RESOURCE_SUBSETS = dict(
l3_interfaces=L3_interfacesFacts, l3_interfaces=L3_interfacesFacts,
l2_interfaces=L2_interfacesFacts, l2_interfaces=L2_interfacesFacts,
lldp_interfaces=Lldp_interfacesFacts, lldp_interfaces=Lldp_interfacesFacts,
acl_interfaces=Acl_interfacesFacts,
) )
@ -68,9 +70,11 @@ class Facts(FactsBase):
:return: the facts gathered :return: the facts gathered
""" """
if self.VALID_RESOURCE_SUBSETS: if self.VALID_RESOURCE_SUBSETS:
self.get_network_resources_facts(FACT_RESOURCE_SUBSETS, resource_facts_type, data) self.get_network_resources_facts(
FACT_RESOURCE_SUBSETS, resource_facts_type, data)
if self.VALID_LEGACY_GATHER_SUBSETS: if self.VALID_LEGACY_GATHER_SUBSETS:
self.get_network_legacy_facts(FACT_LEGACY_SUBSETS, legacy_facts_type) self.get_network_legacy_facts(
FACT_LEGACY_SUBSETS, legacy_facts_type)
return self.ansible_facts, self._warnings return self.ansible_facts, self._warnings

View file

@ -0,0 +1,408 @@
#!/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 nxos_acl_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'
}
DOCUMENTATION = """
---
module: nxos_acl_interfaces
version_added: '2.10'
short_description: Add and remove Access Control Lists on interfaces in NX-OS platform
description: Add and remove Access Control Lists on interfaces in NX-OS platform
author: Adharsh Srivats Rangarajan (@adharshsrivatsr)
notes:
- Tested against NX-OS 7.3.(0)D1(1) on VIRL
options:
running_config:
description:
- Used to parse given commands into structured format, only in parsed state
type: str
config:
description: A list of interfaces to be configured with ACLs
type: list
elements: dict
suboptions:
name:
description: Name of the interface
type: str
required: true
access_groups:
description: List of address family indicators with ACLs to be configured on the interface
type: list
elements: dict
suboptions:
afi:
description: Address Family Indicator of the ACLs to be configured
type: str
required: true
choices: ['ipv4','ipv6']
acls:
description: List of Access Control Lists for the interface
type: list
elements: dict
suboptions:
name:
description: Name of the ACL to be added/removed
type: str
required: true
direction:
description: Direction to be applied for the ACL
type: str
required: true
choices: ['in','out']
port:
description: Use ACL as port policy.
type: bool
state:
description: The state the configuration should be left in
type: str
choices:
- deleted
- gathered
- merged
- overridden
- rendered
- replaced
- parsed
default: merged
"""
EXAMPLES = """# Using merged
# Before state:
# ------------
#
- name: Merge ACL interfaces configuration
nxos_acl_interfaces:
config:
- name: Ethernet1/2
access_groups:
- afi: ipv6
acls:
- name: ACL1v6
direction: in
- name: Eth1/5
access_groups:
- afi: ipv4
acls:
- name: PortACL
direction: in
port: true
- name: ACL1v4
direction: out
- afi: ipv6
acls:
- name: ACL1v6
direction: in
state: merged
# After state:
# ------------
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ip port access-group PortACL in
# ip access-group ACL1v4 out
# ipv6 traffic-filter ACL1v6 in
# Using replaced
# Before state:
# ------------
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ip port access-group PortACL in
# ip access-group ACL1v4 out
# ipv6 traffic-filter ACL1v6 in
- name: Replace interface configuration with given configuration
nxos_acl_interfaces:
config:
- name: Eth1/5
access_groups:
- afi: ipv4
acls:
- name: NewACLv4
direction: out
- name: Ethernet1/3
access_groups:
- afi: ipv6
acls:
- name: NewACLv6
direction: in
port: true
state: replaced
# After state:
# ------------
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/3
# ipv6 port traffic-filter NewACLv6 in
# interface Ethernet1/5
# ip access-group NewACLv4 out
# Using overridden
# Before state:
# ------------
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ip port access-group PortACL in
# ip access-group ACL1v4 out
# ipv6 traffic-filter ACL1v6 in
- name: Override interface configuration with given configuration
nxos_acl_interfaces:
config:
- name: Ethernet1/3
access_groups:
- afi: ipv4
acls:
- name: ACL1v4
direction: out
- name: PortACL
port: true
direction: in
- afi: ipv6
acls:
- name: NewACLv6
direction: in
port: true
state: overridden
# After state:
# ------------
# interface Ethernet1/3
# ip access-group ACL1v4 out
# ip port access-group PortACL in
# ipv6 port traffic-filter NewACLv6 in
# Using deleted
# Before state:
# -------------
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ip port access-group PortACL in
# ip access-group ACL1v4 out
# ipv6 traffic-filter ACL1v6 in
- name: Delete ACL configuration on interfaces
nxos_acl_interfaces:
config:
- name: Ethernet1/5
access_groups:
- afi: ipv6
- afi: ipv4
acls:
- name: ACL1v4
direction: out
- name: Ethernet1/2
state: deleted
# After state:
# -------------
# interface Ethernet1/2
# interface Ethernet1/5
# ip port access-group PortACL in
# ip access-group ACL1v4 out
# ipv6 traffic-filter ACL1v6 in
# Using parsed
- name: Parse given configuration into structured format
nxos_acl_interfaces:
running_config: |
interface Ethernet1/2
ipv6 traffic-filter ACL1v6 in
interface Ethernet1/5
ipv6 traffic-filter ACL1v6 in
ip access-group ACL1v4 out
ip port access-group PortACL in
state: parsed
# returns
# parsed:
# - name: Ethernet1/2
# access_groups:
# - afi: ipv6
# acls:
# - name: ACL1v6
# direction: in
# - name: Ethernet1/5
# access_groups:
# - afi: ipv4
# acls:
# - name: PortACL
# direction: in
# port: True
# - name: ACL1v4
# direction: out
# - afi: ipv6
# acls:
# - name: ACL1v6
# direction: in
# Using gathered:
# Before state:
# ------------
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ipv6 traffic-filter ACL1v6 in
# ip access-group ACL1v4 out
# ip port access-group PortACL in
- name: Gather existing configuration from device
nxos_acl_interfaces:
config:
state: gathered
# returns
# gathered:
# - name: Ethernet1/2
# access_groups:
# - afi: ipv6
# acls:
# - name: ACL1v6
# direction: in
# - name: Ethernet1/5
# access_groups:
# - afi: ipv4
# acls:
# - name: PortACL
# direction: in
# port: True
# - name: ACL1v4
# direction: out
# - afi: ipv6
# acls:
# - name: ACL1v6
# direction: in
# Using rendered
- name: Render required configuration to be pushed to the device
nxos_acl_interfaces:
config:
- name: Ethernet1/2
access_groups:
- afi: ipv6
acls:
- name: ACL1v6
direction: in
- name: Ethernet1/5
access_groups:
- afi: ipv4
acls:
- name: PortACL
direction: in
port: true
- name: ACL1v4
direction: out
- afi: ipv6
acls:
- name: ACL1v6
direction: in
state: rendered
# returns
# rendered:
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ipv6 traffic-filter ACL1v6 in
# ip access-group ACL1v4 out
# ip port access-group PortACL in
"""
RETURN = """
before:
description: The configuration prior to the model invocation.
returned: always
type: dict
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: dict
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: ['interface Ethernet1/2', 'ipv6 traffic-filter ACL1v6 out', 'ip port access-group PortACL in']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.nxos.argspec.acl_interfaces.acl_interfaces import Acl_interfacesArgs
from ansible.module_utils.network.nxos.config.acl_interfaces.acl_interfaces import Acl_interfaces
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
module = AnsibleModule(argument_spec=Acl_interfacesArgs.argument_spec,
supports_check_mode=True)
result = Acl_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -30,7 +30,8 @@ from ansible.module_utils._text import to_bytes, to_text
class TerminalModule(TerminalBase): class TerminalModule(TerminalBase):
terminal_stdout_re = [ terminal_stdout_re = [
re.compile(br'[\r\n](?!\s*<)?(\x1b\S+)*[a-zA-Z_0-9]{1}[a-zA-Z0-9-_.]*[>|#](?:\s*)(\x1b\S+)*$'), re.compile(
br'[\r\n](?!\s*<)?(\x1b\S+)*[a-zA-Z_0-9]{1}[a-zA-Z0-9-_.]*[>|#](?:\s*)(\x1b\S+)*$'),
re.compile(br'[\r\n]?[a-zA-Z0-9]{1}[a-zA-Z0-9-_.]*\(.+\)#(?:\s*)$') re.compile(br'[\r\n]?[a-zA-Z0-9]{1}[a-zA-Z0-9-_.]*\(.+\)#(?:\s*)$')
] ]
@ -49,7 +50,12 @@ class TerminalModule(TerminalBase):
re.compile(br"user not present"), re.compile(br"user not present"),
re.compile(br"invalid (.+?)at '\^' marker", re.I), re.compile(br"invalid (.+?)at '\^' marker", re.I),
re.compile(br"configuration not allowed .+ at '\^' marker"), re.compile(br"configuration not allowed .+ at '\^' marker"),
re.compile(br"[B|b]aud rate of console should be.* (\d*) to increase [a-z]* level", re.I), re.compile(
br"[B|b]aud rate of console should be.* (\d*) to increase [a-z]* level", re.I),
re.compile(br"cannot apply non-existing acl policy to interface", re.I),
re.compile(br"Duplicate sequence number", re.I),
re.compile(
br"Cannot apply ACL to an interface that is a port-channel member", re.I)
] ]
def on_become(self, passwd=None): def on_become(self, passwd=None):
@ -70,18 +76,22 @@ class TerminalModule(TerminalBase):
cmd = {u'command': u'enable'} cmd = {u'command': u'enable'}
if passwd: if passwd:
cmd[u'prompt'] = to_text(r"(?i)[\r\n]?Password: $", errors='surrogate_or_strict') cmd[u'prompt'] = to_text(
r"(?i)[\r\n]?Password: $", errors='surrogate_or_strict')
cmd[u'answer'] = passwd cmd[u'answer'] = passwd
cmd[u'prompt_retry_check'] = True cmd[u'prompt_retry_check'] = True
try: try:
self._exec_cli_command(to_bytes(json.dumps(cmd), errors='surrogate_or_strict')) self._exec_cli_command(
to_bytes(json.dumps(cmd), errors='surrogate_or_strict'))
prompt = self._get_prompt() prompt = self._get_prompt()
if prompt is None or not prompt.strip().endswith(b'enable#'): if prompt is None or not prompt.strip().endswith(b'enable#'):
raise AnsibleConnectionFailure('failed to elevate privilege to enable mode still at prompt [%s]' % prompt) raise AnsibleConnectionFailure(
'failed to elevate privilege to enable mode still at prompt [%s]' % prompt)
except AnsibleConnectionFailure as e: except AnsibleConnectionFailure as e:
prompt = self._get_prompt() prompt = self._get_prompt()
raise AnsibleConnectionFailure('unable to elevate privilege to enable mode, at prompt [%s] with error: %s' % (prompt, e.message)) raise AnsibleConnectionFailure(
'unable to elevate privilege to enable mode, at prompt [%s] with error: %s' % (prompt, e.message))
def on_unbecome(self): def on_unbecome(self):
prompt = self._get_prompt() prompt = self._get_prompt()

View file

@ -0,0 +1,2 @@
---
testcase: "*"

View file

@ -0,0 +1,2 @@
dependencies:
- prepare_nxos_tests

View file

@ -0,0 +1,20 @@
---
- name: collect cli test cases
find:
paths: "{{ role_path }}/tests/cli"
patterns: "{{ testcase }}.yml"
connection: local
register: test_cases
- set_fact:
test_cases:
files: "{{ test_cases.files }}"
- name: set test_items
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
- name: run test cases (connection=network_cli)
include: "{{ test_case_to_run }} ansible_connection=network_cli connection={{ cli }}"
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

View file

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

View file

@ -0,0 +1,90 @@
---
- debug:
msg: "Start nxos_acl_interfaces deleted integration tests connection = {{ansible_connection}}"
- include_tasks: populate_config.yaml
- block:
- name: Delete single ACL from an interface
nxos_acl_interfaces:
config:
- name: Ethernet1/5
access_groups:
- afi: ipv6
acls:
- name: ACL1v6
direction: in
state: deleted
register: result
- assert:
that:
- "result.changed == true"
- "'interface Ethernet1/5' in result.commands"
- "'no ipv6 traffic-filter ACL1v6 in' in result.commands"
- "result.commands | length ==2"
- name: Delete all ACLs of given AFI from an interface
nxos_acl_interfaces:
config:
- name: Ethernet1/5
access_groups:
- afi: ipv4
state: deleted
register: result
- assert:
that:
- "result.changed == true"
- "'interface Ethernet1/5' in result.commands"
- "'no ip port access-group PortACL in' in result.commands"
- "'no ip access-group ACL1v4 out' in result.commands"
- "result.commands | length ==3"
- name: Delete all ACLs configuration from given interface
nxos_acl_interfaces: &deleted
config:
- name: Ethernet1/2
state: deleted
register: result
- assert:
that:
- "result.changed == True"
- "'interface Ethernet1/2' in result.commands"
- "'no ipv6 traffic-filter ACL1v6 in' in result.commands"
- "result.commands | length == 2"
- include_tasks: populate_config.yaml
- name: Delete all ACLs from all interfaces (from all interfaces)
nxos_acl_interfaces:
config:
state: deleted
register: result
- name: Gather acl interfaces facts
nxos_facts: &facts
gather_subset:
- "!all"
- "!min"
gather_network_resources: acl_interfaces
- assert:
that:
- "result.changed == True"
- "ansible_facts.network_resources.acl_interfaces == result.after"
- name: Gather acls facts
nxos_facts: *facts
- name: Idempotence - deleted
nxos_acl_interfaces: *deleted
register: result
- assert:
that:
- "result.changed == false"
always:
- include_tasks: remove_config.yaml

View file

@ -0,0 +1,34 @@
---
- debug:
msg: Start nxos_acl_interfaces gathered integration tests connection={{ansible_connection}}"
- include_tasks: populate_config.yaml
- block:
- name: Gather acl interfaces facts
nxos_facts: &facts
gather_subset:
- "!all"
- "!min"
gather_network_resources: acl_interfaces
- name: Gathered
nxos_acl_interfaces: &gathered
state: gathered
register: result
- assert:
that:
- "result.changed == false"
- "ansible_facts.network_resources.acl_interfaces == result.gathered"
- name: Idempotence - Gathered
nxos_acl_interfaces: *gathered
register: result
- assert:
that:
- "result.changed == false"
always:
- include_tasks: remove_config.yaml

View file

@ -0,0 +1,63 @@
---
- debug:
msg: "Start nxos_acl_interfaces merged integration tests connection = {{ansible_connection}}"
- include_tasks: populate_acl.yaml
- block:
- name: Gather acl interfaces facts
nxos_facts:
gather_subset:
- "!all"
- "!min"
gather_network_resources: acl_interfaces
- name: Merged
nxos_acl_interfaces: &merged
config:
- name: Ethernet1/2
access_groups:
- afi: ipv6
acls:
- name: ACL1v6
direction: in
- name: Eth1/5
access_groups:
- afi: ipv4
acls:
- name: PortACL
direction: in
port: True
- name: ACL1v4
direction: out
- afi: ipv6
acls:
- name: ACL1v6
direction: in
state: merged
register: result
- assert:
that:
- "result.changed == True"
- "'interface Ethernet1/2' in result.commands"
- "'ipv6 traffic-filter ACL1v6 in' in result.commands"
- "'interface Ethernet1/5' in result.commands"
- "'ip port access-group PortACL in' in result.commands"
- "'ip access-group ACL1v4 out' in result.commands"
- "'ipv6 traffic-filter ACL1v6 in' in result.commands"
- "result.commands | length == 6 "
- name: Idempotence - Merged
nxos_acl_interfaces: *merged
register: result
- assert:
that:
- "result.changed == false"
always:
- include_tasks: remove_config.yaml

View file

@ -0,0 +1,68 @@
---
- debug:
msg: "Start nxos_acl_interfaces overridden integration tests connection = {{ansible_connection}}"
- include_tasks: populate_config.yaml
- block:
- name: Overridden
nxos_acl_interfaces: &overridden
config:
- name: Ethernet1/3
access_groups:
- afi: ipv4
acls:
- name: ACL1v4
direction: out
- name: PortACL
port: true
direction: in
- afi: ipv6
acls:
- name: NewACLv6
direction: in
port: true
state: overridden
register: result
- assert:
that:
- "result.changed == True"
- "'interface Ethernet1/2' in result.commands"
- "'no ipv6 traffic-filter ACL1v6 in' in result.commands"
- "'interface Ethernet1/5' in result.commands"
- "'no ip access-group ACL1v4 out' in result.commands"
- "'no ip port access-group PortACL in' in result.commands"
- "'no ipv6 traffic-filter ACL1v6 in' in result.commands"
- "'interface Ethernet1/3' in result.commands"
- "'ip access-group ACL1v4 out' in result.commands"
- "'ip port access-group PortACL in' in result.commands"
- "'ipv6 port traffic-filter NewACLv6 in' in result.commands"
- "result.commands | length == 10"
- name: Gather acl_interfaces post facts
nxos_facts: &facts
gather_subset:
- "!all"
- "!min"
gather_network_resources: acl_interfaces
- name: Gather acls post facts
nxos_facts: *facts
- assert:
that:
- "ansible_facts.network_resources.acl_interfaces == result.after"
- name: Idempotence - overridden
nxos_acl_interfaces: *overridden
register: result
- assert:
that:
- "result.changed == false"
always:
- include_tasks: remove_config.yaml

View file

@ -0,0 +1,40 @@
---
- debug:
msg: Start nxos_acl_interfaces parsed integration tests connection={{ansible_connection}}"
- include_tasks: populate_config.yaml
- block:
- name: Gather acl interfaces facts
nxos_facts: &facts
gather_subset:
- "!all"
- "!min"
gather_network_resources: acl_interfaces
- name: Parsed
nxos_acl_interfaces: &parsed
running_config: |
interface Ethernet1/2
ipv6 traffic-filter ACL1v6 in
interface Ethernet1/5
ipv6 traffic-filter ACL1v6 in
ip access-group ACL1v4 out
ip port access-group PortACL in
state: parsed
register: result
- assert:
that:
- "result.changed == false"
- "result.parsed == parsed"
- name: Idempotence - Parsed
nxos_acl_interfaces: *parsed
register: result
- assert:
that: "result.changed == false"
always:
- include_tasks: remove_config.yaml

View file

@ -0,0 +1,9 @@
---
- name: Adding base configuration
cli_config:
config: |
ip access-list ACL1v4
ip access-list NewACLv4
ipv6 access-list ACL1v6
ipv6 access-list NewACLv6
ip access-list PortACL

View file

@ -0,0 +1,15 @@
---
- name: Adding base configuration
cli_config:
config: |
ip access-list ACL1v4
ip access-list NewACLv4
ipv6 access-list ACL1v6
ipv6 access-list NewACLv6
ip access-list PortACL
interface Ethernet1/2
ipv6 traffic-filter ACL1v6 in
interface Ethernet1/5
ip port access-group PortACL in
ip access-group ACL1v4 out
ipv6 traffic-filter ACL1v6 in

View file

@ -0,0 +1,21 @@
---
- name: Remove config
cli_config:
config: |
no ip access-list ACL1v4
no ip access-list NewACLv4
no ip access-list PortACL
no ipv6 access-list ACL1v6
no ipv6 access-list ACL1v6
interface Ethernet1/2
no ipv6 traffic-filter ACL1v6 in
interface Ethernet1/5
no ip access-group ACL1v4 out
no ip port access-group PortACL in
no ipv6 traffic-filter ACL1v6 in
interface Ethernet1/3
no ipv6 port traffic-filter NewACLv6 in
no ip access-group ACL1v4 out
no ip port access-group PortACL in
interface Ethernet1/4
no ip access-group NewACLv4 out

View file

@ -0,0 +1,48 @@
---
- debug:
msg: "Start nxos_acl_interfaces rendered tests connection={{ ansible_connection }}"
- name: Rendered
nxos_acl_interfaces: &rendered
config:
- name: Ethernet1/2
access_groups:
- afi: ipv6
acls:
- name: ACL1v6
direction: in
- name: Ethernet1/5
access_groups:
- afi: ipv4
acls:
- name: PortACL
direction: in
port: True
- name: ACL1v4
direction: out
- afi: ipv6
acls:
- name: ACL1v6
direction: in
state: rendered
register: result
- assert:
that:
- "result.changed == false"
- "'interface Ethernet1/2' in result.rendered"
- "'ipv6 traffic-filter ACL1v6 in' in result.rendered"
- "'interface Ethernet1/5' in result.rendered"
- "'ipv6 traffic-filter ACL1v6 in' in result.rendered"
- "'ip access-group ACL1v4 out' in result.rendered"
- "'ip port access-group PortACL in' in result.rendered"
- "result.rendered | length == 6"
- name: Idempotence - Rendered
nxos_acl_interfaces: *rendered
register: result
- assert:
that:
- "result.changed == false"

View file

@ -0,0 +1,60 @@
---
- debug:
msg: "Start nxos_acl_interfaces replaced integration tests connection = {{ansible_connection}}"
- include_tasks: populate_config.yaml
- block:
- name: Replaced
nxos_acl_interfaces: &replaced
config:
- name: Eth1/5
access_groups:
- afi: ipv4
acls:
- name: NewACLv4
direction: out
- name: Ethernet1/3
access_groups:
- afi: ipv6
acls:
- name: NewACLv6
direction: in
port: true
state: replaced
register: result
- assert:
that:
- "result.changed==True"
- "'interface Ethernet1/5' in result.commands"
- "'no ip access-group ACL1v4 out' in result.commands"
- "'no ip port access-group PortACL in' in result.commands"
- "'no ipv6 traffic-filter ACL1v6 in' in result.commands"
- "'ip access-group NewACLv4 out' in result.commands"
- "'interface Ethernet1/3' in result.commands"
- "'ipv6 port traffic-filter NewACLv6 in' in result.commands"
- "result.commands|length==7"
- name: Gather acl_interfaces post facts
nxos_facts:
gather_subset:
- "!all"
- "!min"
gather_network_resources: acl_interfaces
- assert:
that:
- "ansible_facts.network_resources.acl_interfaces == result.after"
- name: Idempotence - Replaced
nxos_acl_interfaces: *replaced
register: result
- assert:
that:
- "result.changed == false"
always:
- include_tasks: remove_config.yaml

View file

@ -0,0 +1,99 @@
---
- debug:
msg: "Start nxos_acl_interfaces round trip integration tests connection = {{ansible_connection}}"
- include_tasks: populate_config.yaml
- block:
- name: RTT- Apply provided configuration
nxos_acl_interfaces:
config:
- name: Ethernet1/2
access_groups:
- afi: ipv6
acls:
- name: ACL1v6
direction: in
- name: Eth1/5
access_groups:
- afi: ipv4
acls:
- name: PortACL
direction: in
port: True
- name: ACL1v4
direction: out
- afi: ipv6
acls:
- name: ACL1v6
direction: in
state: merged
- name: Gather interfaces facts
nxos_facts:
gather_subset:
- "!all"
- "!min"
gather_network_resources:
- acl_interfaces
- name: Apply configuration to be reverted
nxos_acl_interfaces:
config:
- name: Eth1/4
access_groups:
- afi: ipv4
acls:
- name: NewACLv4
direction: out
- name: Ethernet1/3
access_groups:
- afi: ipv6
acls:
- name: NewACLv6
direction: in
port: true
state: overridden
register: result
- name: Assert that changes were applied
assert:
that:
- "result.changed==True"
- "'interface Ethernet1/2' in result.commands"
- "'no ipv6 traffic-filter ACL1v6 in' in result.commands"
- "'interface Ethernet1/5' in result.commands"
- "'no ip access-group ACL1v4 out' in result.commands"
- "'no ip port access-group PortACL in' in result.commands"
- "'no ipv6 traffic-filter ACL1v6 in' in result.commands"
- "'interface Ethernet1/4' in result.commands"
- "'ip access-group NewACLv4 out' in result.commands"
- "'interface Ethernet1/3' in result.commands"
- "'ipv6 port traffic-filter NewACLv6 in' in result.commands"
- name: Revert back to base configuration using facts round trip
nxos_acl_interfaces:
config: "{{ ansible_facts['network_resources']['acl_interfaces'] }}"
state: overridden
register: result
- name: Assert that config was reverted
assert:
that:
- "result.changed==True"
- "'interface Ethernet1/2' in result.commands"
- "'ipv6 traffic-filter ACL1v6 in' in result.commands"
- "'interface Ethernet1/3' in result.commands"
- "'no ipv6 port traffic-filter NewACLv6 in' in result.commands"
- "'interface Ethernet1/4' in result.commands"
- "'no ip access-group NewACLv4 out' in result.commands"
- "'interface Ethernet1/5' in result.commands"
- "'ip access-group ACL1v4 out' in result.commands"
- "'ip port access-group PortACL in' in result.commands"
- "'ipv6 traffic-filter ACL1v6 in' in result.commands"
always:
- include_tasks: remove_config.yaml

View file

@ -0,0 +1,21 @@
---
parsed:
- access_groups:
- acls:
- direction: in
name: ACL1v6
afi: ipv6
name: Ethernet1/2
- access_groups:
- acls:
- direction: out
name: ACL1v4
- direction: in
name: PortACL
port: true
afi: ipv4
- acls:
- direction: in
name: ACL1v6
afi: ipv6
name: Ethernet1/5

View file

@ -0,0 +1,2 @@
interface Ethernet1/2
ip access-group ACL1v4 out

View file

@ -73,7 +73,8 @@ class TestNxosModule(ModuleTestCase):
retvals = {} retvals = {}
for model in models: for model in models:
retvals[model] = self.execute_module(failed, changed, commands, sort, device=model) retvals[model] = self.execute_module(
failed, changed, commands, sort, device=model)
return retvals return retvals
@ -87,19 +88,19 @@ class TestNxosModule(ModuleTestCase):
else: else:
result = self.changed(changed) result = self.changed(changed)
self.assertEqual(result['changed'], changed, result) self.assertEqual(result['changed'], changed, result)
if commands is not None and len(commands) > 0:
if commands is not None:
if sort: if sort:
self.assertEqual(sorted(commands), sorted(result['commands']), result['commands']) self.assertEqual(sorted(commands), sorted(
result['commands']), result['commands'])
else: else:
self.assertEqual(commands, result['commands'], result['commands']) self.assertEqual(
commands, result['commands'], result['commands'])
return result return result
def failed(self): def failed(self):
with self.assertRaises(AnsibleFailJson) as exc: with self.assertRaises(AnsibleFailJson) as exc:
self.module.main() self.module.main()
result = exc.exception.args[0] result = exc.exception.args[0]
self.assertTrue(result['failed'], result) self.assertTrue(result['failed'], result)
return result return result

View file

@ -0,0 +1,303 @@
#
# (c) 2019, Ansible by Red Hat, inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.modules.network.nxos import nxos_acl_interfaces
from units.compat.mock import patch, MagicMock
from units.modules.utils import set_module_args
from .nxos_module import TestNxosModule, load_fixture
class TestNxosAclInterfacesModule(TestNxosModule):
module = nxos_acl_interfaces
def setUp(self):
super(TestNxosAclInterfacesModule, self).setUp()
self.mock_get_config = patch(
'ansible.module_utils.network.common.network.Config.get_config')
self.get_config = self.mock_get_config.start()
self.mock_load_config = patch(
'ansible.module_utils.network.common.network.Config.load_config')
self.load_config = self.mock_load_config.start()
self.mock_get_resource_connection_config = patch(
'ansible.module_utils.network.common.cfg.base.get_resource_connection'
)
self.get_resource_connection_config = self.mock_get_resource_connection_config.start(
)
self.mock_get_resource_connection_facts = patch(
'ansible.module_utils.network.common.facts.facts.get_resource_connection'
)
self.get_resource_connection_facts = self.mock_get_resource_connection_facts.start()
self.mock_edit_config = patch(
'ansible.module_utils.network.nxos.config.acl_interfaces.acl_interfaces.Acl_interfaces.edit_config'
)
self.edit_config = self.mock_edit_config.start()
self.mock_execute_show_command = patch(
'ansible.module_utils.network.nxos.facts.acl_interfaces.acl_interfaces.Acl_interfacesFacts.get_device_data'
)
self.execute_show_command = self.mock_execute_show_command.start()
def tearDown(self):
super(TestNxosAclInterfacesModule, self).tearDown()
self.mock_get_resource_connection_config.stop()
self.mock_get_resource_connection_facts.stop()
self.mock_edit_config.stop()
self.mock_get_config.stop()
self.mock_load_config.stop()
self.mock_execute_show_command.stop()
def load_fixtures(self, commands=None, device=''):
def load_from_file(*args, **kwargs):
output = '''interface Ethernet1/2\n ip access-group ACL1v4 out\n interface Ethernet1/4\n ipv6 port traffic-filter ACL2v6 in\n'''
return output
self.execute_show_command.side_effect = load_from_file
def test_nxos_acl_interfaces_merged(self):
set_module_args(
dict(config=[
dict(name="Ethernet1/3",
access_groups=[
dict(afi="ipv4",
acls=[
dict(
name="ACL1v4",
direction="in",
)
]
)
]
)
], state="merged"))
commands = ['interface Ethernet1/3',
'ip access-group ACL1v4 in']
self.execute_module(changed=True, commands=commands)
def test_nxos_acl_interfaces_merged_idempotent(self):
set_module_args(
dict(config=[
dict(name="Ethernet1/2",
access_groups=[
dict(afi="ipv4",
acls=[
dict(
name="ACL1v4",
direction="out",
)
]
)
]
),
dict(name="Ethernet1/4",
access_groups=[
dict(afi="ipv6",
acls=[
dict(
name="ACL2v6",
direction="in",
port=True
)
]
)
]
),
], state="merged"))
self.execute_module(changed=False, commands=[])
def test_nxos_acl_interfaces_replaced(self):
set_module_args(
dict(config=[
dict(name="Ethernet1/2",
access_groups=[
dict(afi="ipv6",
acls=[
dict(
name="ACL1v6",
direction="in",
port=True
)
]
)
]
),
dict(name="Ethernet1/5",
access_groups=[
dict(afi="ipv4",
acls=[
dict(
name="ACL2v4",
direction="in",
port=True
)
]
)
]
)
], state="replaced"))
commands = ['interface Ethernet1/2', 'no ip access-group ACL1v4 out',
'ipv6 port traffic-filter ACL1v6 in', 'interface Ethernet1/5', 'ip port access-group ACL2v4 in']
self.execute_module(changed=True, commands=commands)
def test_nxos_acl_interfaces_replaced_idempotent(self):
set_module_args(
dict(config=[
dict(name="Ethernet1/2",
access_groups=[
dict(afi="ipv4",
acls=[
dict(
name="ACL1v4",
direction="out",
)
]
)
]
)], state="replaced"))
self.execute_module(changed=False, commands=[])
def test_nxos_acl_interfaces_overridden(self):
set_module_args(
dict(config=[
dict(name="Ethernet1/3",
access_groups=[
dict(afi="ipv4",
acls=[
dict(
name="ACL2v4",
direction="out"
),
dict(
name="PortACL",
direction="in",
port=True
),
]
)
]
)], state="overridden"))
commands = ['interface Ethernet1/2', 'no ip access-group ACL1v4 out', 'interface Ethernet1/4',
'no ipv6 port traffic-filter ACL2v6 in', 'interface Ethernet1/3', 'ip access-group ACL2v4 out', 'ip port access-group PortACL in']
self.execute_module(changed=True, commands=commands)
def test_nxos_acl_interfaces_overridden_idempotent(self):
set_module_args(
dict(config=[
dict(name="Ethernet1/2",
access_groups=[
dict(afi="ipv4",
acls=[
dict(
name="ACL1v4",
direction="out",
)
]
)
]
),
dict(name="Ethernet1/4",
access_groups=[
dict(afi="ipv6",
acls=[
dict(
name="ACL2v6",
direction="in",
port=True
)
]
)
]
),
], state="overridden"))
self.execute_module(changed=False, commands=[])
def test_nxos_acl_interfaces_deletedname(self):
set_module_args(
dict(config=[dict(name="Ethernet1/2")], state="deleted"))
commands = ['interface Ethernet1/2', 'no ip access-group ACL1v4 out']
self.execute_module(changed=True, commands=commands)
def test_nxos_acl_interfaces_deletedafi(self):
set_module_args(
dict(config=[dict(name="Ethernet1/2", access_groups=[
dict(afi="ipv4")
])], state="deleted"))
commands = ['interface Ethernet1/2', 'no ip access-group ACL1v4 out']
self.execute_module(changed=True, commands=commands)
def test_nxos_acl_interfaces_deletedacl(self):
set_module_args(
dict(config=[dict(name="Ethernet1/2", access_groups=[
dict(afi="ipv4", acls=[
dict(
name="ACL1v4",
direction="out"
)
])
])], state="deleted"))
commands = ['interface Ethernet1/2', 'no ip access-group ACL1v4 out']
self.execute_module(changed=True, commands=commands)
def test_nxos_acl_interfaces_rendered(self):
set_module_args(
dict(config=[
dict(name="Ethernet1/2",
access_groups=[
dict(afi="ipv4",
acls=[
dict(
name="ACL1v4",
direction="out",
)
]
)
]
),
dict(name="Ethernet1/4",
access_groups=[
dict(afi="ipv6",
acls=[
dict(
name="ACL2v6",
direction="in",
port=True
)
]
)
]
),
], state="rendered"))
commands = ['interface Ethernet1/2', 'ip access-group ACL1v4 out',
'interface Ethernet1/4', 'ipv6 port traffic-filter ACL2v6 in']
result = self.execute_module(changed=False)
self.assertEqual(sorted(result['rendered']), sorted(
commands), result['rendered'])
def test_nxos_acl_interfaces_parsed(self):
set_module_args(dict(running_config='''interface Ethernet1/2\n ip access-group ACL1v4 out\n interface Ethernet1/4\n \
ipv6 port traffic-filter ACL2v6 in''',
state="parsed"))
result = self.execute_module(changed=False)
compare_list = [{'access_groups': [{'acls': [{'direction': 'out', 'name': 'ACL1v4'}], 'afi': 'ipv4'}], 'name': 'Ethernet1/2'},
{'access_groups': [{'acls': [{'direction': 'in', 'name': 'ACL2v6', 'port': True}], 'afi': 'ipv6'}], 'name': 'Ethernet1/4'}]
self.assertEqual(result['parsed'],
compare_list, result['parsed'])
def test_nxos_acl_interfaces_gathered(self):
set_module_args(dict(config=[], state="gathered"))
result = self.execute_module(changed=False)
compare_list = [{'access_groups': [{'acls': [{'direction': 'out', 'name': 'ACL1v4'}], 'afi': 'ipv4'}], 'name': 'Ethernet1/2'},
{'access_groups': [{'acls': [{'direction': 'in', 'name': 'ACL2v6', 'port': True}], 'afi': 'ipv6'}], 'name': 'Ethernet1/4'}]
self.assertEqual(result['gathered'],
compare_list, result['gathered'])