Add [junos_lacp_interfaces] resource module (#59708)

* Add [junos_lacp_interfaces] resource module

*  Add new resource module junos_lacp_interfaces.
*  Targets model https://github.com/ansible-network/resource_module_models/pull/36

* Fix sanity test failure
This commit is contained in:
Ganesh Nalawade 2019-08-12 11:24:34 +05:30 committed by GitHub
parent e4dc17fa9f
commit fdf48ed0b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1413 additions and 1 deletions

View file

@ -9,6 +9,7 @@ The arg spec for the junos facts module.
CHOICES = [ CHOICES = [
'all', 'all',
'interfaces', 'interfaces',
'lacp_interfaces',
'lag_interfaces', 'lag_interfaces',
'l2_interfaces', 'l2_interfaces',
'l3_interfaces', 'l3_interfaces',

View file

@ -0,0 +1,53 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the junos_lacp_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Lacp_interfacesArgs(object):
"""The arg spec for the junos_lacp_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {'config': {'elements': 'dict',
'options': {'force_up': {'type': 'bool'},
'name': {'type': 'str'},
'period': {'choices': ['fast', 'slow']},
'port_priority': {'type': 'int'},
'sync_reset': {'choices': ['disable', 'enable'],
'type': 'str'},
'system': {'options': {'mac': {'type': 'dict',
'options': {'address': {'type': 'str'}}},
'priority': {'type': 'int'}},
'type': 'dict'}},
'type': 'list'},
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
'default': 'merged',
'type': 'str'}}

View file

@ -0,0 +1,253 @@
#
# -*- 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 junos_lacp_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, get_xml_conf_arg
from ansible.module_utils.network.junos.facts.facts import Facts
from ansible.module_utils.network.junos.junos import locked_config, load_config, commit_configuration, discard_changes, tostring
from ansible.module_utils.network.common.netconf import build_root_xml_node, build_child_xml_node, build_subtree
class Lacp_interfaces(ConfigBase):
"""
The junos_lacp_interfaces class
"""
gather_subset = [
'!all',
'!min',
]
gather_network_resources = [
'lacp_interfaces',
]
def __init__(self, module):
super(Lacp_interfaces, self).__init__(module)
def get_lacp_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)
lacp_interfaces_facts = facts['ansible_network_resources'].get('lacp_interfaces')
if not lacp_interfaces_facts:
return []
return lacp_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_lacp_interfaces_facts = self.get_lacp_interfaces_facts()
commands.extend(self.set_config(existing_lacp_interfaces_facts))
if commands:
if not self._module.check_mode:
self._connection.edit_config(commands)
result['changed'] = True
result['commands'] = commands
changed_lacp_interfaces_facts = self.get_lacp_interfaces_facts()
result['before'] = existing_lacp_interfaces_facts
if result['changed']:
result['after'] = changed_lacp_interfaces_facts
result['warnings'] = warnings
return result
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {'changed': False}
existing_lacp_interfaces_facts = self.get_lacp_interfaces_facts()
config_xmls = self.set_config(existing_lacp_interfaces_facts)
with locked_config(self._module):
for config_xml in to_list(config_xmls):
diff = load_config(self._module, config_xml, [])
commit = not self._module.check_mode
if diff:
if commit:
commit_configuration(self._module)
else:
discard_changes(self._module)
result['changed'] = True
if self._module._diff:
result['diff'] = {'prepared': diff}
result['xml'] = config_xmls
changed_lacp_interfaces_facts = self.get_lacp_interfaces_facts()
result['before'] = existing_lacp_interfaces_facts
if result['changed']:
result['after'] = changed_lacp_interfaces_facts
return result
def set_config(self, existing_lacp_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_lacp_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
"""
root = build_root_xml_node('interfaces')
state = self._module.params['state']
if state == 'overridden':
config_xmls = self._state_overridden(want, have)
elif state == 'deleted':
config_xmls = self._state_deleted(want, have)
elif state == 'merged':
config_xmls = self._state_merged(want, have)
elif state == 'replaced':
config_xmls = self._state_replaced(want, have)
for xml in config_xmls:
root.append(xml)
return tostring(root)
def _state_replaced(self, want, have):
""" The xml configuration generator when state is replaced
:rtype: A list
:returns: the xml configuration necessary to migrate the current configuration
to the desired configuration
"""
intf_xml = []
intf_xml.extend(self._state_deleted(want, have))
intf_xml.extend(self._state_merged(want, have))
return intf_xml
def _state_overridden(self, want, have):
""" The xml configuration generator when state is overridden
:rtype: A list
:returns: the xml configuration necessary to migrate the current configuration
to the desired configuration
"""
interface_xmls_obj = []
# replace interface config with data in want
interface_xmls_obj.extend(self._state_replaced(want, have))
# delete interface config if interface in have not present in want
delete_obj = []
for have_obj in have:
for want_obj in want:
if have_obj['name'] == want_obj['name']:
break
else:
delete_obj.append(have_obj)
if delete_obj:
interface_xmls_obj.extend(self._state_deleted(delete_obj, have))
return interface_xmls_obj
def _state_merged(self, want, have):
""" The xml configuration generator when state is merged
:rtype: A list
:returns: the xml configuration necessary to merge the provided into
the current configuration
"""
intf_xml = []
for config in want:
lacp_intf_name = config['name']
lacp_intf_root = build_root_xml_node('interface')
build_child_xml_node(lacp_intf_root, 'name', lacp_intf_name)
if lacp_intf_name.startswith('ae'):
element = build_subtree(lacp_intf_root, 'aggregated-ether-options/lacp')
if config['period']:
build_child_xml_node(element, 'periodic', config['period'])
if config['sync_reset']:
build_child_xml_node(element, 'sync-reset', config['sync_reset'])
system = config['system']
if system:
mac = system.get('mac')
if mac:
if mac.get('address'):
build_child_xml_node(element, 'system-id', mac['address'])
if system.get('priority'):
build_child_xml_node(element, 'system-priority', system['priority'])
intf_xml.append(lacp_intf_root)
elif config['port_priority'] or config['force_up'] is not None:
element = build_subtree(lacp_intf_root, 'ether-options/ieee-802.3ad/lacp')
build_child_xml_node(element, 'port-priority', config['port_priority'])
if config['force_up'] is False:
build_child_xml_node(element, 'force-up', None, {'delete': 'delete'})
else:
build_child_xml_node(element, 'force-up')
intf_xml.append(lacp_intf_root)
return intf_xml
def _state_deleted(self, want, have):
""" The xml configuration generator when state is deleted
:rtype: A list
:returns: the xml configuration necessary to remove the current configuration
of the provided objects
"""
intf_xml = []
intf_obj = want
if not intf_obj:
# delete lag interfaces attribute for all the interface
intf_obj = have
for config in intf_obj:
lacp_intf_name = config['name']
lacp_intf_root = build_root_xml_node('interface')
build_child_xml_node(lacp_intf_root, 'name', lacp_intf_name)
if lacp_intf_name.startswith('ae'):
element = build_subtree(lacp_intf_root, 'aggregated-ether-options/lacp')
build_child_xml_node(element, 'periodic', None, {'delete': 'delete'})
build_child_xml_node(element, 'sync-reset', None, {'delete': 'delete'})
build_child_xml_node(element, 'system-id', None, {'delete': 'delete'})
build_child_xml_node(element, 'system-priority', None, {'delete': 'delete'})
else:
element = build_subtree(lacp_intf_root, 'ether-options/ieee-802.3ad/lacp')
build_child_xml_node(element, 'port-priority', None, {'delete': 'delete'})
build_child_xml_node(element, 'force-up', None, {'delete': 'delete'})
intf_xml.append(lacp_intf_root)
return intf_xml

View file

@ -13,6 +13,7 @@ from ansible.module_utils.network.junos.argspec.facts.facts import FactsArgs
from ansible.module_utils.network.common.facts.facts import FactsBase from ansible.module_utils.network.common.facts.facts import FactsBase
from ansible.module_utils.network.junos.facts.legacy.base import Default, Hardware, Config, Interfaces, OFacts, HAS_PYEZ from ansible.module_utils.network.junos.facts.legacy.base import Default, Hardware, Config, Interfaces, OFacts, HAS_PYEZ
from ansible.module_utils.network.junos.facts.interfaces.interfaces import InterfacesFacts from ansible.module_utils.network.junos.facts.interfaces.interfaces import InterfacesFacts
from ansible.module_utils.network.junos.facts.lacp_interfaces.lacp_interfaces import Lacp_interfacesFacts
from ansible.module_utils.network.junos.facts.lag_interfaces.lag_interfaces import Lag_interfacesFacts from ansible.module_utils.network.junos.facts.lag_interfaces.lag_interfaces import Lag_interfacesFacts
from ansible.module_utils.network.junos.facts.l3_interfaces.l3_interfaces import L3_interfacesFacts from ansible.module_utils.network.junos.facts.l3_interfaces.l3_interfaces import L3_interfacesFacts
from ansible.module_utils.network.junos.facts.lldp_interfaces.lldp_interfaces import Lldp_interfacesFacts from ansible.module_utils.network.junos.facts.lldp_interfaces.lldp_interfaces import Lldp_interfacesFacts
@ -27,6 +28,7 @@ FACT_LEGACY_SUBSETS = dict(
) )
FACT_RESOURCE_SUBSETS = dict( FACT_RESOURCE_SUBSETS = dict(
interfaces=InterfacesFacts, interfaces=InterfacesFacts,
lacp_interfaces=Lacp_interfacesFacts,
lag_interfaces=Lag_interfacesFacts, lag_interfaces=Lag_interfacesFacts,
l2_interfaces=L2_interfacesFacts, l2_interfaces=L2_interfacesFacts,
l3_interfaces=L3_interfacesFacts, l3_interfaces=L3_interfacesFacts,

View file

@ -0,0 +1,112 @@
#
# -*- 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 junos lacp_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
from ansible.module_utils._text import to_bytes
from ansible.module_utils.network.common import utils
from ansible.module_utils.network.junos.argspec.lacp_interfaces.lacp_interfaces import Lacp_interfacesArgs
from ansible.module_utils.six import string_types
try:
from lxml import etree
HAS_LXML = True
except ImportError:
HAS_LXML = False
class Lacp_interfacesFacts(object):
""" The junos lacp_interfaces fact class
"""
def __init__(self, module, subspec='config', options='options'):
self._module = module
self.argument_spec = Lacp_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 data: previously collected configuration as lxml ElementTree root instance
or valid xml sting
:rtype: dictionary
:returns: facts
"""
if not HAS_LXML:
self._module.fail_json(msg='lxml is not installed.')
if not data:
config_filter = """
<configuration>
<interfaces/>
</configuration>
"""
data = connection.get_configuration(filter=config_filter)
if isinstance(data, string_types):
data = etree.fromstring(to_bytes(data, errors='surrogate_then_replace'))
self._resources = data.xpath('configuration/interfaces/interface')
objs = []
for resource in self._resources:
if resource is not None:
obj = self.render_config(self.generated_spec, resource)
if obj:
objs.append(obj)
facts = {}
if objs:
facts['lacp_interfaces'] = []
params = utils.validate_config(self.argument_spec, {'config': objs})
for cfg in params['config']:
facts['lacp_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 ElementTree instance of configuration object
:rtype: dictionary
:returns: The generated config
"""
config = deepcopy(spec)
config['name'] = utils.get_xml_conf_arg(conf, 'name')
config['period'] = utils.get_xml_conf_arg(conf, 'aggregated-ether-options/lacp/periodic')
config['sync_reset'] = utils.get_xml_conf_arg(conf, 'aggregated-ether-options/lacp/sync-reset')
force_up = utils.get_xml_conf_arg(conf, 'ether-options/ieee-802.3ad/lacp/force-up', data='tag')
if force_up:
config['force_up'] = True
config['port_priority'] = utils.get_xml_conf_arg(conf, 'ether-options/ieee-802.3ad/lacp/port-priority')
config['system']['priority'] = utils.get_xml_conf_arg(conf, 'aggregated-ether-options/lacp/system-priority')
address = utils.get_xml_conf_arg(conf, 'aggregated-ether-options/lacp/system-id')
if address:
config['system'].update({'mac': {'address': address}})
lacp_intf_cfg = utils.remove_empties(config)
# if lacp config is not present for interface return empty dict
if len(lacp_intf_cfg) == 1:
return {}
else:
return lacp_intf_cfg

View file

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

View file

@ -0,0 +1,515 @@
#!/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 junos_lacp_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'
}
DOCUMENTATION = """
---
module: junos_lacp_interfaces
version_added: 2.9
short_description: Manage Link Aggregation Control Protocol (LACP) attributes of interfaces on Juniper JUNOS devices.
description:
- This module manages Link Aggregation Control Protocol (LACP) attributes of interfaces on Juniper JUNOS devices.
author: Ganesh Nalawade (@ganeshrn)
options:
config:
description: The list of dictionaries of LACP interfaces options.
type: list
elements: dict
suboptions:
name:
description:
- Name Identifier of the interface or link aggregation group.
type: str
period:
description:
- Timer interval for periodic transmission of LACP packets. If the value is
set to C(fast) the packets are received every second and if the value is
C(slow) the packets are received every 30 seconds. This value is applicable
for aggregate interface only.
type: str
choices: ['fast', 'slow']
sync_reset:
description:
- The argument notifies minimum-link failure out of sync to peer. If the value
is C(disable) it disables minimum-link failure handling at LACP level and if
value is C(enable) it enables minimum-link failure handling at LACP level.
This value is applicable for aggregate interface only.
type: str
choices: ['disable', 'enable']
force_up:
description:
- This is a boolean argument to control if the port should be up in absence
of received link Aggregation Control Protocol Data Unit (LACPDUS).
This value is applicable for member interfaces only.
type: bool
port_priority:
description:
- Priority of the member port. This value is applicable for member interfaces only.
- Refer to vendor documentation for valid values.
type: int
system:
description:
- This dict object contains configurable options related to LACP
system parameters for the link aggregation group.
This value is applicable for aggregate interface only.
type: dict
suboptions:
priority:
description:
- Specifies the system priority to use in LACP negotiations for
the bundle.
- Refer to vendor documentation for valid values.
type: int
mac:
description:
- Specifies the system ID to use in LACP negotiations for
the bundle, encoded as a MAC address.
type: dict
suboptions:
address:
description:
- The system ID to use in LACP negotiations.
type: str
state:
description:
- The state the configuration should be left in.
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
"""
EXAMPLES = """
# Using merged
# Before state:
# -------------
# user@junos01# show interfaces
# ge-0/0/2 {
# ether-options {
# 802.3ad ae4;
# }
# }
# ge-0/0/3 {
# ether-options {
# 802.3ad ae0;
# }
# }
# ae0 {
# description "lag interface merged";
# aggregated-ether-options {
# lacp {
# passive;
# }
# }
# }
# ae4 {
# description "test aggregate interface";
# aggregated-ether-options {
# lacp {
# passive;
# link-protection;
# }
# }
# }
- name: Merge provided configuration with device configuration
junos_lacp_interfaces:
config:
- name: ae0
period: fast
sync_reset: enable
system:
priority: 100
mac:
address: 00:00:00:00:00:02
- name: ge-0/0/3
port_priority: 100
force_up: True
state: merged
# After state:
# -------------
# user@junos01# show interfaces
# ge-0/0/2 {
# ether-options {
# 802.3ad ae4;
# }
# }
# ge-0/0/3 {
# ether-options {
# 802.3ad {
# lacp {
# force-up;
# port-priority 100;
# }
# ae0;
# }
# }
# }
# ae0 {
# description "lag interface merged";
# aggregated-ether-options {
# lacp {
# passive;
# periodic fast;
# sync-reset enable;
# system-priority 100;
# system-id 00:00:00:00:00:02;
# }
# }
# }
# ae4 {
# description "test aggregate interface";
# aggregated-ether-options {
# lacp {
# passive;
# link-protection;
# }
# }
# }
# Using replaced
# Before state:
# -------------
# user@junos01# show interfaces
# ge-0/0/2 {
# ether-options {
# 802.3ad ae4;
# }
# }
# ge-0/0/3 {
# ether-options {
# 802.3ad {
# lacp {
# force-up;
# port-priority 100;
# }
# ae0;
# }
# }
# }
# ae0 {
# description "lag interface merged";
# aggregated-ether-options {
# lacp {
# passive;
# periodic fast;
# sync-reset enable;
# system-priority 100;
# system-id 00:00:00:00:00:02;
# }
# }
# }
# ae4 {
# description "test aggregate interface";
# aggregated-ether-options {
# lacp {
# passive;
# link-protection;
# }
# }
# }
- name: Replace device LACP interfaces configuration with provided configuration
junos_lacp_interfaces:
config:
- name: ae0
period: slow
state: replaced
# After state:
# -------------
# user@junos01# show interfaces
# ge-0/0/2 {
# ether-options {
# 802.3ad ae4;
# }
# }
# ge-0/0/3 {
# ether-options {
# 802.3ad {
# lacp {
# force-up;
# port-priority 100;
# }
# ae0;
# }
# }
# }
# ae0 {
# description "lag interface merged";
# aggregated-ether-options {
# lacp {
# passive;
# periodic slow;
# }
# }
# }
# ae4 {
# description "test aggregate interface";
# aggregated-ether-options {
# lacp {
# passive;
# link-protection;
# }
# }
# }
# Using overridden
# Before state:
# -------------
# user@junos01# show interfaces
# ge-0/0/2 {
# ether-options {
# 802.3ad ae4;
# }
# }
# ge-0/0/3 {
# ether-options {
# 802.3ad {
# lacp {
# force-up;
# port-priority 100;
# }
# ae0;
# }
# }
# }
# ae0 {
# description "lag interface merged";
# aggregated-ether-options {
# lacp {
# passive;
# periodic slow;
# }
# }
# }
# ae4 {
# description "test aggregate interface";
# aggregated-ether-options {
# lacp {
# passive;
# link-protection;
# }
# }
# }
- name: Overrides all device LACP interfaces configuration with provided configuration
junos_lacp_interfaces:
config:
- name: ae0
system:
priority: 300
mac:
address: 00:00:00:00:00:03
- name: ge-0/0/2
port_priority: 200
force_up: False
state: overridden
# After state:
# -------------
# user@junos01# show interfaces
# ge-0/0/2 {
# ether-options {
# 802.3ad {
# lacp {
# port-priority 200;
# }
# ae4;
# }
# }
# }
# ge-0/0/3 {
# ether-options {
# 802.3ad {
# lacp {
# force-up;
# port-priority 100;
# }
# ae0;
# }
# }
# }
# ae0 {
# description "lag interface merged";
# aggregated-ether-options {
# lacp {
# passive;
# system-priority 300;
# system-id 00:00:00:00:00:03;
# }
# }
# }
# ae4 {
# description "test aggregate interface";
# aggregated-ether-options {
# lacp {
# passive;
# link-protection;
# }
# }
# }
# Using deleted
# Before state:
# -------------
# user@junos01# show interfaces
# ge-0/0/2 {
# ether-options {
# 802.3ad {
# lacp {
# port-priority 200;
# }
# ae4;
# }
# }
# }
# ge-0/0/3 {
# ether-options {
# 802.3ad {
# lacp {
# force-up;
# port-priority 100;
# }
# ae0;
# }
# }
# }
# ae0 {
# description "lag interface merged";
# aggregated-ether-options {
# lacp {
# passive;
# system-priority 300;
# system-id 00:00:00:00:00:03;
# }
# }
# }
# ae4 {
# description "test aggregate interface";
# aggregated-ether-options {
# lacp {
# passive;
# link-protection;
# }
# }
# }
- name: "Delete LACP interfaces attributes of given interfaces (Note: This won't delete the interface itself)"
junos_lacp_interfaces:
config:
- name: ae0
- name: ge-0/0/3
- name: ge-0/0/2
state: deleted
# After state:
# -------------
# user@junos01# show interfaces
# ge-0/0/2 {
# ether-options {
# 802.3ad ae4;
# }
# }
# ge-0/0/3 {
# ether-options {
# 802.3ad ae0;
# }
# }
# ae0 {
# description "lag interface merged";
# aggregated-ether-options {
# lacp {
# passive;
# }
# }
# }
# ae4 {
# description "test aggregate interface";
# aggregated-ether-options {
# lacp {
# passive;
# link-protection;
# }
# }
# }
"""
RETURN = """
before:
description: The configuration prior to the model invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The resulting configuration model invocation.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample: ['command 1', 'command 2', 'command 3']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.junos.argspec.lacp_interfaces.lacp_interfaces import Lacp_interfacesArgs
from ansible.module_utils.network.junos.config.lacp_interfaces.lacp_interfaces import Lacp_interfaces
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
module = AnsibleModule(argument_spec=Lacp_interfacesArgs.argument_spec,
supports_check_mode=True)
result = Lacp_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,2 @@
dependencies:
# - prepare_junos_tests

View file

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

View file

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

View file

@ -0,0 +1,16 @@
---
- debug:
msg: "Start junos_lacp_interfaces base config ansible_connection={{ ansible_connection }}"
- name: Configure base lag interface
junos_config:
lines:
- set interfaces ae1 description "Configured by Ansible"
- set interfaces ae2 description "Configured by Ansible"
- set interfaces ge-0/0/1 ether-options 802.3ad ae1
- set interfaces ge-0/0/2 ether-options 802.3ad ae1
- set interfaces ge-0/0/3 ether-options 802.3ad ae2
- set interfaces ge-0/0/4 ether-options 802.3ad ae2
- debug:
msg: "End junos_lacp_interfaces base config ansible_connection={{ ansible_connection }}"

View file

@ -0,0 +1,17 @@
---
- debug:
msg: "Start junos_nterfaces deleted remove interface config ansible_connection={{ ansible_connection }}"
- name: "Setup - remove interface config"
junos_config:
lines:
- delete interfaces ae1
- delete interfaces ae2
- delete interfaces ge-0/0/1
- delete interfaces ge-0/0/2
- delete interfaces ge-0/0/3
- delete interfaces ge-0/0/4
- delete interfaces lo0
- debug:
msg: "End junos_nterfaces deleted remove interface config ansible_connection={{ ansible_connection }}"

View file

@ -0,0 +1,97 @@
---
- debug:
msg: "START junos_lacp_interfaces deleted integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _base_config.yaml
- set_fact:
expected_deleted_output:
- name: ae2
period: slow
sync_reset: disable
system:
priority: 200
mac:
address: 00:00:00:00:00:04
- name: ge-0/0/3
port_priority: 300
- block:
- name: Configure initial state for interface
junos_lacp_interfaces: &initial
config:
- name: ae1
period: fast
sync_reset: enable
system:
priority: 100
mac:
address: 00:00:00:00:00:02
- name: ge-0/0/1
port_priority: 100
force_up: True
- name: ae2
period: slow
sync_reset: disable
system:
priority: 200
mac:
address: 00:00:00:00:00:04
- name: ge-0/0/3
port_priority: 300
force_up: False
state: merged
register: result
- name: Delete the provided interface configuration from running configuration
junos_lacp_interfaces: &deleted
config:
- name: ae1
- name: ge-0/0/1
state: deleted
register: result
- name: Assert the configuration is reflected on host
assert:
that:
- "{{ expected_deleted_output | symmetric_difference(result['after']) |length == 0 }}"
- name: Delete the provided lacp interface configuration from running configuration (IDEMPOTENT)
junos_lacp_interfaces: *deleted
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
- name: Configure initial state for interface
junos_lacp_interfaces: *initial
register: result
- name: Delete the all lacp interface configuration from running configuration
junos_lacp_interfaces:
state: deleted
register: result
- name: Assert the configuration is reflected on host
assert:
that:
- "{{ result['after'] == [] }}"
- name: Delete the all lacp interfaces configuration from running configuration (IDEMPOTENT)
junos_lacp_interfaces:
state: deleted
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
always:
- include_tasks: _remove_config.yaml
- debug:
msg: "END junos_lacp_interfaces deleted integration tests on connection={{ ansible_connection }}"

View file

@ -0,0 +1,56 @@
---
- debug:
msg: "START junos_lacp_interfaces merged integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _base_config.yaml
- set_fact:
expected_merged_output:
- name: ae1
period: fast
sync_reset: enable
system:
priority: 100
mac:
address: 00:00:00:00:00:02
- name: ge-0/0/1
port_priority: 100
force_up: True
- block:
- name: Merge the provided configuration with the exisiting running configuration
junos_lacp_interfaces: &merged
config:
- name: ae1
period: fast
sync_reset: enable
system:
priority: 100
mac:
address: 00:00:00:00:00:02
- name: ge-0/0/1
port_priority: 100
force_up: True
state: merged
register: result
- name: Assert the configuration is reflected on host
assert:
that:
- "{{ expected_merged_output | symmetric_difference(result['after']) |length == 0 }}"
- name: Merge the provided configuration with the existing running configuration (IDEMPOTENT)
junos_lacp_interfaces: *merged
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
always:
- include_tasks: _remove_config.yaml
- debug:
msg: "END junos_lacp_interfaces merged integration tests on connection={{ ansible_connection }}"

View file

@ -0,0 +1,76 @@
---
- debug:
msg: "START junos_lacp_interfaces overide integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _base_config.yaml
- set_fact:
expected_overridden_output:
- name: ae1
period: fast
sync_reset: enable
system:
priority: 100
mac:
address: 00:00:00:00:00:01
- block:
- name: Configure initial state for lacp interface
junos_lacp_interfaces:
config:
- name: ae1
period: fast
sync_reset: enable
system:
priority: 100
mac:
address: 00:00:00:00:00:02
- name: ge-0/0/1
port_priority: 100
force_up: True
- name: ae2
period: slow
sync_reset: disable
system:
priority: 200
mac:
address: 00:00:00:00:00:04
- name: ge-0/0/3
port_priority: 300
force_up: False
state: merged
register: result
- name: Override the provided configuration with the exisiting running configuration
junos_lacp_interfaces: &overridden
config:
- name: ae1
period: fast
sync_reset: enable
system:
priority: 100
mac:
address: 00:00:00:00:00:01
state: overridden
register: result
- name: Assert the configuration is reflected on host
assert:
that:
- "{{ expected_overridden_output | symmetric_difference(result['after']) |length == 0 }}"
- name: Override the provided configuration with the existing running configuration (IDEMPOTENT)
junos_lacp_interfaces: *overridden
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
always:
- include_tasks: _remove_config.yaml
- debug:
msg: "END junos_lacp_interfaces override integration tests on connection={{ ansible_connection }}"

View file

@ -0,0 +1,100 @@
---
- debug:
msg: "START junos_lacp_interfaces replaced integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _base_config.yaml
- set_fact:
expected_replaced_output:
- name: ae1
period: slow
sync_reset: disable
system:
priority: 10
mac:
address: 00:00:00:00:00:03
- name: ae2
period: fast
system:
priority: 300
- name: ge-0/0/1
force_up: true
port_priority: 100
- name: ge-0/0/2
port_priority: 250
- name: ge-0/0/3
port_priority: 300
- name: ge-0/0/4
port_priority: 400
force_up: true
- block:
- name: Configure initial state for lacp interface
junos_lacp_interfaces:
config:
- name: ae1
period: fast
sync_reset: enable
system:
priority: 100
mac:
address: 00:00:00:00:00:02
- name: ge-0/0/1
port_priority: 100
force_up: True
- name: ae2
period: slow
sync_reset: disable
system:
priority: 200
mac:
address: 00:00:00:00:00:04
- name: ge-0/0/3
port_priority: 300
force_up: False
state: merged
register: result
- name: Replace the provided configuration with the exisiting running configuration
junos_lacp_interfaces: &replaced
config:
- name: ae1
period: slow
sync_reset: disable
system:
priority: 10
mac:
address: 00:00:00:00:00:03
- name: ge-0/0/2
port_priority: 250
force_up: False
- name: ae2
period: fast
system:
priority: 300
- name: ge-0/0/4
port_priority: 400
force_up: True
state: replaced
register: result
- name: Assert the configuration is reflected on host
assert:
that:
- "{{ expected_replaced_output | symmetric_difference(result['after']) |length == 0 }}"
- name: Replace the provided configuration with the existing running configuration (IDEMPOTENT)
junos_lacp_interfaces: *replaced
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
always:
- include_tasks: _remove_config.yaml
- debug:
msg: "END junos_lacp_interfaces replaced integration tests on connection={{ ansible_connection }}"

View file

@ -0,0 +1,90 @@
---
- debug:
msg: "START junos_lacp_interfaces round trip integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _base_config.yaml
- set_fact:
expected_revert_output:
- name: ae1
period: slow
sync_reset: disable
system:
priority: 10
mac:
address: 00:00:00:00:00:03
- name: ae2
period: fast
system:
priority: 300
- name: ge-0/0/2
port_priority: 250
force_up: True
- name: ge-0/0/4
port_priority: 400
force_up: True
- block:
- name: Apply the provided configuration (base config)
junos_lacp_interfaces:
config:
- name: ae1
period: slow
sync_reset: disable
system:
priority: 10
mac:
address: 00:00:00:00:00:03
- name: ge-0/0/2
port_priority: 250
force_up: False
- name: ae2
period: fast
system:
priority: 300
- name: ge-0/0/4
port_priority: 400
force_up: True
state: merged
register: base_config
- name: Gather interfaces facts
junos_facts:
gather_subset:
- default
gather_network_resources:
- lacp_interfaces
- name: Apply the provided configuration (config to be reverted)
junos_lacp_interfaces:
config:
- name: ae1
period: fast
sync_reset: enable
system:
priority: 100
mac:
address: 00:00:00:00:00:01
state: merged
register: result
- name: Assert that changes were applied
assert:
that: "result['changed'] == true"
- name: Revert back to base config using facts round trip
junos_lacp_interfaces:
config: "{{ ansible_facts['network_resources']['lacp_interfaces'] }}"
state: overridden
register: revert
- name: Assert that config was reverted
assert:
that: "{{ expected_revert_output | symmetric_difference(revert['after']) |length == 0 }}"
always:
- include_tasks: _remove_config.yaml
- debug:
msg: "END junos_lacp_interfaces round trip integration tests on connection={{ ansible_connection }}"