Resource module for ios_lag_interfaces (#60672)

Signed-off-by: Sumit Jaiswal <sjaiswal@redhat.com>
This commit is contained in:
Sumit Jaiswal 2019-08-19 20:41:00 +05:30 committed by GitHub
parent 31bfcd4745
commit 04ef376ab2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1182 additions and 2 deletions

View file

@ -27,6 +27,8 @@ class FactsArgs(object):
'!l2_interfaces',
'vlans',
'!vlans',
'lag_interfaces',
'!lag_interfaces',
]
argument_spec = {

View file

@ -0,0 +1,52 @@
#
# -*- 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_lag_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Lag_interfacesArgs(object):
def __init__(self, **kwargs):
pass
argument_spec = {'config': {'elements': 'dict',
'options': {'name': {'required': True, 'type': 'str'},
'members': {'elements': 'dict',
'options': {
'member': {'type': 'str'},
'mode': {'choices': ['auto', 'on', 'desirable',
'active', 'passive'],
'type': 'str', 'required': True},
'link': {'type': 'int'}
},
'type': 'list'}},
'type': 'list'},
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
'default': 'merged',
'type': 'str'}}

View file

@ -0,0 +1,293 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""
The ios_lag_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
import re
from ansible.module_utils.network.common import utils
from ansible.module_utils.network.common.cfg.base import ConfigBase
from ansible.module_utils.network.common.utils import to_list
from ansible.module_utils.network.ios.facts.facts import Facts
from ansible.module_utils.network.ios.utils.utils import dict_to_set
from ansible.module_utils.network.ios.utils.utils import filter_dict_having_none_value, remove_duplicate_interface
class Lag_interfaces(ConfigBase):
"""
The ios_lag_interfaces class
"""
gather_subset = [
'!all',
'!min',
]
gather_network_resources = [
'lag_interfaces',
]
def __init__(self, module):
super(Lag_interfaces, self).__init__(module)
def get_lag_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)
lag_interfaces_facts = facts['ansible_network_resources'].get('lag_interfaces')
if not lag_interfaces_facts:
return []
return lag_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_lag_interfaces_facts = self.get_lag_interfaces_facts()
commands.extend(self.set_config(existing_lag_interfaces_facts))
if commands:
if not self._module.check_mode:
self._connection.edit_config(commands)
result['changed'] = True
result['commands'] = commands
changed_lag_interfaces_facts = self.get_lag_interfaces_facts()
result['before'] = existing_lag_interfaces_facts
if result['changed']:
result['after'] = changed_lag_interfaces_facts
result['warnings'] = warnings
return result
def set_config(self, existing_lag_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_lag_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']
module = self._module
if state == 'overridden':
commands = self._state_overridden(want, have, module)
elif state == 'deleted':
commands = self._state_deleted(want, have)
elif state == 'merged':
commands = self._state_merged(want, have, module)
elif state == 'replaced':
commands = self._state_replaced(want, have, 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_interface in interface.get('members'):
for each in have:
if each.get('members'):
for every in each.get('members'):
match = False
if every['member'] == each_interface['member']:
match = True
break
else:
continue
if match:
have_dict = filter_dict_having_none_value(interface, each)
commands.extend(self._clear_config(dict(), have_dict))
commands.extend(self._set_config(interface, each, module))
elif each.get('name') == each_interface['member']:
have_dict = filter_dict_having_none_value(interface, each)
commands.extend(self._clear_config(dict(), have_dict))
commands.extend(self._set_config(interface, each, module))
break
# 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 = []
for interface in want:
for each_interface in interface.get('members'):
for each in have:
if each.get('members'):
for every in each.get('members'):
match = False
if every['member'] == each_interface['member']:
match = True
break
else:
commands.extend(self._clear_config(interface, each))
continue
if match:
have_dict = filter_dict_having_none_value(interface, each)
commands.extend(self._clear_config(dict(), have_dict))
commands.extend(self._set_config(interface, each, module))
elif each.get('name') == each_interface['member']:
have_dict = filter_dict_having_none_value(interface, each)
commands.extend(self._clear_config(dict(), have_dict))
commands.extend(self._set_config(interface, each, module))
break
# 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_interface in interface.get('members'):
for each in have:
if each.get('members'):
for every in each.get('members'):
if every['member'] == each_interface['member']:
break
elif each.get('name') == each_interface['member']:
break
else:
continue
commands.extend(self._set_config(interface, each, module))
return commands
def _state_deleted(self, want, have):
""" The command generator when state is deleted
: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.get('name') == interface['name']:
break
else:
continue
commands.extend(self._clear_config(interface, each))
else:
for each in have:
commands.extend(self._clear_config(dict(), each))
return commands
def remove_command_from_config_list(self, interface, cmd, commands):
# To delete the passed config
if interface not in commands:
commands.append(interface)
commands.append('no %s' % cmd)
return commands
def add_command_to_config_list(self, interface, cmd, commands):
# To set the passed config
if interface not in commands:
commands.append(interface)
commands.append(cmd)
return commands
def _set_config(self, want, have, module):
# Set the interface config based on the want and have config
commands = []
# To remove keys with None values from want dict
want = utils.remove_empties(want)
# Get the diff b/w want and have
want_dict = dict_to_set(want)
have_dict = dict_to_set(have)
diff = want_dict - have_dict
# To get the channel-id from lag port-channel name
lag_config = dict(diff).get('members')
channel_name = re.search(r'(\d+)', want.get('name'))
if channel_name:
channel_id = channel_name.group()
else:
module.fail_json(msg="Lag Interface Name is not correct!")
if lag_config:
for each in lag_config:
each = dict(each)
each_interface = 'interface {0}'.format(each.get('member'))
if have.get('name') == want['members'][0]['member'] or have.get('name').lower().startswith('po'):
if each.get('mode'):
cmd = 'channel-group {0} mode {1}'.format(channel_id, each.get('mode'))
self.add_command_to_config_list(each_interface, cmd, commands)
elif each.get('link'):
cmd = 'channel-group {0} link {1}'.format(channel_id, each.get('link'))
self.add_command_to_config_list(each_interface, cmd, commands)
return commands
def _clear_config(self, want, have):
# Delete the interface config based on the want and have config
commands = []
if have.get('members'):
for each in have['members']:
interface = 'interface ' + each['member']
if want.get('members'):
if each.get('member') and each.get('member') != want['members'][0]['member']:
self.remove_command_from_config_list(interface, 'channel-group', commands)
elif each.get('member'):
self.remove_command_from_config_list(interface, 'channel-group', commands)
return commands

View file

@ -18,6 +18,7 @@ from ansible.module_utils.network.common.facts.facts import FactsBase
from ansible.module_utils.network.ios.facts.interfaces.interfaces import InterfacesFacts
from ansible.module_utils.network.ios.facts.l2_interfaces.l2_interfaces import L2_InterfacesFacts
from ansible.module_utils.network.ios.facts.vlans.vlans import VlansFacts
from ansible.module_utils.network.ios.facts.lag_interfaces.lag_interfaces import Lag_interfacesFacts
from ansible.module_utils.network.ios.facts.legacy.base import Default, Hardware, Interfaces, Config
@ -31,7 +32,8 @@ FACT_LEGACY_SUBSETS = dict(
FACT_RESOURCE_SUBSETS = dict(
interfaces=InterfacesFacts,
l2_interfaces=L2_InterfacesFacts,
vlans=VlansFacts
vlans=VlansFacts,
lag_interfaces=Lag_interfacesFacts,
)

View file

@ -0,0 +1,118 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""
The ios lag_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.ios.utils.utils import get_interface_type, normalize_interface
from ansible.module_utils.network.ios.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs
class Lag_interfacesFacts(object):
""" The ios_lag_interfaces fact class
"""
def __init__(self, module, subspec='config', options='options'):
self._module = module
self.argument_spec = Lag_interfacesArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for interfaces
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
objs = []
if not data:
data = connection.get('show running-config | section ^interface')
# operate on a collection of resource x
config = data.split('interface ')
for conf in config:
if conf:
obj = self.render_config(self.generated_spec, conf)
if obj:
if not obj.get('members'):
obj.update({'members': []})
objs.append(obj)
# for appending members configured with same channel-group
for each in range(len(objs)):
if each < (len(objs) - 1):
if objs[each]['name'] == objs[each + 1]['name']:
objs[each]['members'].append(objs[each + 1]['members'][0])
del objs[each + 1]
facts = {}
if objs:
facts['lag_interfaces'] = []
params = utils.validate_config(self.argument_spec, {'config': objs})
for cfg in params['config']:
facts['lag_interfaces'].append(utils.remove_empties(cfg))
ansible_facts['ansible_network_resources'].update(facts)
return ansible_facts
def render_config(self, spec, conf):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
config = deepcopy(spec)
match = re.search(r'^(\S+)', conf)
intf = match.group(1)
if get_interface_type(intf) == 'unknown':
return {}
member_config = {}
channel_group = utils.parse_conf_arg(conf, 'channel-group')
if intf.startswith('Gi'):
config['name'] = intf
config['members'] = []
if channel_group:
channel_group = channel_group.split(' ')
id = channel_group[0]
config['name'] = 'Port-channel{0}'.format(str(id))
if 'mode' in channel_group:
mode = channel_group[2]
member_config.update({'mode': mode})
if 'link' in channel_group:
link = channel_group[2]
member_config.update({'link': link})
if member_config.get('mode') or member_config.get('link'):
member_config['member'] = normalize_interface(intf)
config['members'].append(member_config)
return utils.remove_empties(config)

View file

@ -54,7 +54,8 @@ options:
to a given subset. Possible values for this argument include
all and the resources like interfaces, vlans etc.
Can specify a list of values to include a larger subset.
choices: ['all', '!all', 'interfaces', '!interfaces', 'l2_interfaces', '!l2_interfaces', 'vlans', '!vlans']
choices: ['all', '!all', 'interfaces', '!interfaces', 'l2_interfaces', '!l2_interfaces', 'vlans', '!vlans',
'lag_interfaces', '!lag_interfaces']
version_added: "2.9"
"""

View file

@ -0,0 +1,384 @@
#!/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)
##############################################
# WARNING
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#
##############################################
"""
The module file for ios_l3_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: ios_lag_interfaces
version_added: 2.9
short_description: Manage Link Aggregation on Cisco IOS devices.
description: This module manages properties of Link Aggregation Group on Cisco IOS devices.
author: Sumit Jaiswal (@justjais)
notes:
- Tested against Cisco IOSv Version 15.2 on VIRL
- This module works with connection C(network_cli).
See L(IOS Platform Options,../network/user_guide/platform_ios.html).
options:
config:
description: A list of link aggregation group configurations.
type: list
elements: dict
suboptions:
name:
description:
- ID of Ethernet Channel of interfaces.
- Refer to vendor documentation for valid port values.
type: str
required: True
members:
description:
- Interface options for the link aggregation group.
type: list
suboptions:
member:
description:
- Interface member of the link aggregation group.
type: str
mode:
description:
- Etherchannel Mode of the interface for link aggregation.
type: str
choices:
- 'auto'
- 'on'
- 'desirable'
- 'active'
- 'passive'
link:
description:
- Assign a link identifier used for load-balancing.
- Refer to vendor documentation for valid values.
- NOTE, parameter only supported on Cisco IOS XE platform.
type: int
state:
description:
- The state the configuration should be left in
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
"""
EXAMPLES = """
---
# Using merged
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface GigabitEthernet0/1
# shutdown
# interface GigabitEthernet0/2
# shutdown
# interface GigabitEthernet0/3
# shutdown
# interface GigabitEthernet0/4
# shutdown
- name: Merge provided configuration with device configuration
ios_lag_interfaces:
config:
- name: 10
members:
- member: GigabitEthernet0/1
mode: auto
- member: GigabitEthernet0/2
mode: auto
- name: 20
members:
- member: GigabitEthernet0/3
mode: on
- name: 30
members:
- member: GigabitEthernet0/4
mode: active
state: merged
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/2
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 20 mode on
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
# Using overridden
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/2
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 20 mode on
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
- name: Override device configuration of all interfaces with provided configuration
ios_lag_interfaces:
config:
- name: 20
members:
- member: GigabitEthernet0/2
mode: auto
- member: GigabitEthernet0/3
mode: auto
state: overridden
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# interface GigabitEthernet0/2
# shutdown
# channel-group 20 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 20 mode auto
# interface GigabitEthernet0/4
# shutdown
# Using replaced
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/2
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 20 mode on
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
- name: Replaces device configuration of listed interfaces with provided configuration
ios_lag_interfaces:
config:
- name: 40
members:
- member: GigabitEthernet0/3
mode: auto
state: replaced
# After state:
# ------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface Port-channel40
# interface GigabitEthernet0/1
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/2
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 40 mode on
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
# Using Deleted
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/2
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 20 mode on
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
- name: "Delete LAG attributes of given interfaces (Note: This won't delete the interface itself)"
ios_lag_interfaces:
config:
- name: 10
- name: 20
state: deleted
# After state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# interface GigabitEthernet0/2
# shutdown
# interface GigabitEthernet0/3
# shutdown
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
# Using Deleted without any config passed
#"(NOTE: This will delete all of configured LLDP module attributes)"
#
# Before state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/2
# shutdown
# channel-group 10 mode auto
# interface GigabitEthernet0/3
# shutdown
# channel-group 20 mode on
# interface GigabitEthernet0/4
# shutdown
# channel-group 30 mode active
- name: "Delete all configured LAG attributes for interfaces (Note: This won't delete the interface itself)"
ios_lag_interfaces:
state: deleted
# After state:
# -------------
#
# vios#show running-config | section ^interface
# interface Port-channel10
# interface Port-channel20
# interface Port-channel30
# interface GigabitEthernet0/1
# shutdown
# interface GigabitEthernet0/2
# shutdown
# interface GigabitEthernet0/3
# shutdown
# interface GigabitEthernet0/4
# shutdown
"""
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/1', 'channel-group 1 mode active']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.ios.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs
from ansible.module_utils.network.ios.config.lag_interfaces.lag_interfaces import Lag_interfaces
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
module = AnsibleModule(argument_spec=Lag_interfacesArgs.argument_spec,
supports_check_mode=True)
result = Lag_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,3 @@
---
testcase: "[^_].*"
test_items: []

View file

@ -0,0 +1 @@
dependencies: []

View file

@ -0,0 +1,24 @@
---
- name: Collect all cli test cases
find:
paths: "{{ role_path }}/tests/cli"
patterns: "{{ testcase }}.yaml"
use_regex: true
register: test_cases
delegate_to: localhost
- name: Set test_items
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
delegate_to: localhost
- name: Get the IOS version
ios_facts:
gather_subset: all
- name: Run test case (connection=network_cli)
include: "{{ test_case_to_run }}"
vars:
ansible_connection: network_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,11 @@
---
- name: Populate Config
cli_config:
config: "{{ lines }}"
vars:
lines: |
interface GigabitEthernet 0/1
channel-group 1 mode active
interface GigabitEthernet 0/2
channel-group 1 mode passive
when: ansible_net_version != "15.6(2)T"

View file

@ -0,0 +1,11 @@
---
- name: Remove Config
cli_config:
config: "{{ lines }}"
vars:
lines: |
interface GigabitEthernet 0/1
no channel-group
interface GigabitEthernet 0/2
no channel-group
when: ansible_net_version != "15.6(2)T"

View file

@ -0,0 +1,41 @@
---
- debug:
msg: "Start Deleted integration state for ios_lag_interfaces ansible_connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate_config.yaml
- block:
- name: Delete attributes of all configured interfaces
ios_lag_interfaces: &deleted
state: deleted
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ deleted['commands'] | symmetric_difference(result['commands']) | length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ deleted['before'] | symmetric_difference(result['before']) | length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ deleted['after'] | symmetric_difference(result['after']) | length == 0 }}"
- name: Delete attributes of all configured interfaces (IDEMPOTENT)
ios_lag_interfaces: *deleted
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result.changed == false"
when: ansible_net_version != "15.6(2)T"
always:
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,46 @@
---
- debug:
msg: "START Merged ios_lag_interfaces state for integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- block:
- name: Merge provided configuration with device configuration
ios_lag_interfaces: &merged
config:
- name: Port-channel1
members:
- member: GigabitEthernet0/1
mode: active
- member: GigabitEthernet0/2
mode: passive
state: merged
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ merged['commands'] | symmetric_difference(result['commands']) | length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ merged['before'] | symmetric_difference(result['before']) | length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ merged['after'] | symmetric_difference(result['after']) | length == 0 }}"
- name: Merge provided configuration with device configuration (IDEMPOTENT)
ios_lag_interfaces: *merged
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
when: ansible_net_version != "15.6(2)T"
always:
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,46 @@
---
- debug:
msg: "START Overridden ios_lag_interfaces state for integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate_config.yaml
- block:
- name: Override device configuration of all interfaces with provided configuration
ios_lag_interfaces: &overridden
config:
- name: Port-channel10
members:
- member: GigabitEthernet0/2
mode: passive
state: overridden
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ overridden['commands'] | symmetric_difference(result['commands']) | length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ overridden['before'] | symmetric_difference(result['before']) | length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ overridden['after'] | symmetric_difference(result['after']) | length == 0 }}"
- name: Override device configuration of all interfaces with provided configuration (IDEMPOTENT)
ios_lag_interfaces: *overridden
register: result
- name: Assert that task was idempotent
assert:
that:
- "result['changed'] == false"
when: ansible_net_version != "15.6(2)T"
always:
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,50 @@
---
- debug:
msg: "START Replaced ios_lag_interfaces state for integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _populate_config.yaml
- block:
- name: Replaces device configuration of listed interfaces with provided configuration
ios_lag_interfaces: &replaced
config:
- name: Port-channel2
members:
- member: GigabitEthernet0/1
mode: active
- name: Port-channel40
members:
- member: GigabitEthernet0/2
mode: active
state: replaced
register: result
- name: Assert that correct set of commands were generated
assert:
that:
- "{{ replaced['commands'] | symmetric_difference(result['commands']) | length == 0 }}"
- name: Assert that before dicts are correctly generated
assert:
that:
- "{{ replaced['before'] | symmetric_difference(result['before']) | length == 0 }}"
- name: Assert that after dict is correctly generated
assert:
that:
- "{{ replaced['after'] | symmetric_difference(result['after']) | length == 0 }}"
- name: Replaces device configuration of listed interfaces with provided configuration (IDEMPOTENT)
ios_lag_interfaces: *replaced
register: result
- name: Assert that task was idempotent
assert:
that:
- "result['changed'] == false"
when: ansible_net_version != "15.6(2)T"
always:
- include_tasks: _remove_config.yaml

View file

@ -0,0 +1,93 @@
---
merged:
before:
- name: GigabitEthernet0/0
- name: GigabitEthernet0/1
- name: GigabitEthernet0/2
commands:
- "interface GigabitEthernet0/1"
- "channel-group 1 mode active"
- "interface GigabitEthernet0/2"
- "channel-group 1 mode passive"
after:
- name: GigabitEthernet0/0
- members:
- member: GigabitEthernet0/1
mode: active
- member: GigabitEthernet0/2
mode: passive
name: Port-channel1
replaced:
before:
- name: GigabitEthernet0/0
- members:
- member: GigabitEthernet0/1
mode: active
- member: GigabitEthernet0/2
mode: passive
name: Port-channel1
commands:
- "interface GigabitEthernet0/1"
- "channel-group 2 mode active"
- "interface GigabitEthernet0/2"
- "channel-group 40 mode active"
after:
- name: GigabitEthernet0/0
- members:
- member: GigabitEthernet0/1
mode: active
name: Port-channel2
- members:
- member: GigabitEthernet0/2
mode: active
name: Port-channel40
overridden:
before:
- name: GigabitEthernet0/0
- members:
- member: GigabitEthernet0/1
mode: active
- member: GigabitEthernet0/2
mode: passive
name: Port-channel1
commands:
- "interface GigabitEthernet0/1"
- "no channel-group"
- "interface GigabitEthernet0/2"
- "channel-group 10 mode passive"
after:
- name: GigabitEthernet0/0
- name: GigabitEthernet0/1
- members:
- member: GigabitEthernet0/2
mode: passive
name: Port-channel10
deleted:
before:
- name: GigabitEthernet0/0
- members:
- member: GigabitEthernet0/1
mode: active
- member: GigabitEthernet0/2
mode: passive
name: Port-channel1
commands:
- "interface GigabitEthernet0/1"
- "no channel-group"
- "interface GigabitEthernet0/2"
- "no channel-group"
after:
- name: GigabitEthernet0/0
- name: GigabitEthernet0/1
- name: GigabitEthernet0/2