Add [junos_lag_interfaces] resource module (#59011)

* Add junos_lag_interfaces resource module

*  Add new resource module junos_lag_interfaces.
*  Targets model https://github.com/ansible-network/resource_module_models/pull/29
*  Deprecate junos_linkagg module

* Fix CI issues

* Fix junos integration test cases

* Fail if base lag interface is not configured
This commit is contained in:
Ganesh Nalawade 2019-07-23 11:53:15 +05:30 committed by GitHub
parent 3bad4d6a50
commit 1092ee55d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1118 additions and 6 deletions

View file

@ -8,7 +8,8 @@ The arg spec for the junos facts module.
"""
CHOICES = [
'all',
'interfaces'
'interfaces',
'lag_interfaces'
]

View file

@ -0,0 +1,51 @@
#
# -*- 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_lag_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Lag_interfacesArgs(object):
"""The arg spec for the junos_lag_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {'config': {'elements': 'dict',
'options': {'members': {'elements': 'dict',
'options': {'link_type': {'choices': ['primary',
'backup']},
'member': {'type': 'str'}},
'type': 'list'},
'mode': {'choices': ['active', 'passive']},
'name': {'required': True, 'type': 'str'},
'link_protection': {'type': 'bool'}},
'type': 'list'},
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
'default': 'merged',
'type': 'str'}}

View file

@ -0,0 +1,252 @@
#
# -*- 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_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
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 Lag_interfaces(ConfigBase):
"""
The junos_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}
warnings = list()
existing_lag_interfaces_facts = self.get_lag_interfaces_facts()
config_xmls = self.set_config(existing_lag_interfaces_facts)
with locked_config(self._module):
for config_xml in to_list(config_xmls):
diff = load_config(self._module, config_xml, warnings)
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_lag_interfaces_facts = self.get_lag_interfaces_facts()
result['before'] = existing_lag_interfaces_facts
if result['changed']:
result['after'] = changed_lag_interfaces_facts
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
"""
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 = []
config_filter = """
<configuration>
<interfaces/>
</configuration>
"""
data = self._connection.get_configuration(filter=config_filter)
for config in want:
lag_name = config['name']
# if lag interface not already configured fail module.
if not data.xpath("configuration/interfaces/interface[name='%s']" % lag_name):
self._module.fail_json(msg="lag interface %s not configured, configure interface"
" %s before assigning members to lag" % (lag_name, lag_name))
lag_intf_root = build_root_xml_node('interface')
build_child_xml_node(lag_intf_root, 'name', lag_name)
ether_options_node = build_subtree(lag_intf_root, 'aggregated-ether-options')
if config['mode']:
lacp_node = build_child_xml_node(ether_options_node, 'lacp')
build_child_xml_node(lacp_node, config['mode'])
link_protection = config['link_protection']
if link_protection:
build_child_xml_node(ether_options_node, 'link-protection')
elif link_protection is False:
build_child_xml_node(ether_options_node, 'link-protection', None, {'delete': 'delete'})
intf_xml.append(lag_intf_root)
members = config['members']
for member in members:
lag_member_intf_root = build_root_xml_node('interface')
build_child_xml_node(lag_member_intf_root, 'name', member['member'])
lag_node = build_subtree(lag_member_intf_root, 'ether-options/ieee-802.3ad')
build_child_xml_node(lag_node, 'bundle', config['name'])
link_type = member.get('link_type')
if link_type == "primary":
build_child_xml_node(lag_node, 'primary')
elif link_type == "backup":
build_child_xml_node(lag_node, 'backup')
intf_xml.append(lag_member_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:
lag_name = config['name']
lag_intf_root = build_root_xml_node('interface')
build_child_xml_node(lag_intf_root, 'name', lag_name)
lag_node = build_subtree(lag_intf_root, 'aggregated-ether-options')
build_child_xml_node(lag_node, 'link-protection', None, {'delete': 'delete'})
lacp_node = build_child_xml_node(lag_node, 'lacp')
build_child_xml_node(lacp_node, 'active', None, {'delete': 'delete'})
build_child_xml_node(lacp_node, 'passive', None, {'delete': 'delete'})
intf_xml.append(lag_intf_root)
# delete lag configuration from member interfaces
for interface_obj in have:
if lag_name == interface_obj['name']:
for member in interface_obj.get('members', []):
lag_member_intf_root = build_root_xml_node('interface')
build_child_xml_node(lag_member_intf_root, 'name', member['member'])
lag_node = build_subtree(lag_member_intf_root, 'ether-options/ieee-802.3ad')
build_child_xml_node(lag_node, 'bundle', None, {'delete': 'delete'})
build_child_xml_node(lag_node, 'primary', None, {'delete': 'delete'})
build_child_xml_node(lag_node, 'backup', None, {'delete': 'delete'})
intf_xml.append(lag_member_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.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.lag_interfaces.lag_interfaces import Lag_interfacesFacts
FACT_LEGACY_SUBSETS = dict(
default=Default,
@ -22,6 +23,7 @@ FACT_LEGACY_SUBSETS = dict(
)
FACT_RESOURCE_SUBSETS = dict(
interfaces=InterfacesFacts,
lag_interfaces=Lag_interfacesFacts,
)

View file

@ -0,0 +1,126 @@
#
# -*- 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 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
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.lag_interfaces.lag_interfaces import Lag_interfacesArgs
from ansible.module_utils.six import string_types
try:
from lxml import etree
HAS_LXML = True
except ImportError:
HAS_LXML = False
class Lag_interfacesFacts(object):
""" The junos 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 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['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 ElementTree instance of configuration object
:rtype: dictionary
:returns: The generated config
"""
config = deepcopy(spec)
intf_name = utils.get_xml_conf_arg(conf, 'name')
if intf_name.startswith('ae'):
config['name'] = intf_name
config['members'] = []
for interface_obj in self._resources:
lag_interface_member = utils.get_xml_conf_arg(interface_obj, "ether-options/ieee-802.3ad[bundle='%s']/../../name" % intf_name)
if lag_interface_member:
member_config = {}
member_config['member'] = lag_interface_member
if utils.get_xml_conf_arg(interface_obj, "ether-options/ieee-802.3ad/primary", data='tag'):
member_config['link_type'] = "primary"
elif utils.get_xml_conf_arg(interface_obj, "ether-options/ieee-802.3ad/backup", data='tag'):
member_config['link_type'] = "backup"
if member_config:
config['members'].append(member_config)
for m in ['active', 'passive']:
if utils.get_xml_conf_arg(conf, "aggregated-ether-options/lacp/%s" % m, data='tag'):
config['mode'] = m
break
link_protection = utils.get_xml_conf_arg(conf, "aggregated-ether-options/link-protection", data='tag')
if link_protection:
config['link_protection'] = True
lag_intf_cfg = utils.remove_empties(config)
# if lag interfaces config is not present return empty dict
if len(lag_intf_cfg) == 1:
return {}
else:
return lag_intf_cfg

View file

@ -9,7 +9,7 @@ __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'status': ['deprecated'],
'supported_by': 'network'}
@ -22,6 +22,10 @@ short_description: Manage link aggregation groups on Juniper JUNOS network devic
description:
- This module provides declarative management of link aggregation groups
on Juniper JUNOS network devices.
deprecated:
removed_in: "2.13"
why: Updated modules released with more functionality
alternative: Use M(junos_lag_interfaces) instead.
options:
name:
description:

View file

@ -63,7 +63,7 @@ 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', 'interfaces']
choices: ['all', 'interfaces', 'lag_interfaces']
required: false
version_added: "2.9"
requirements:

View file

@ -0,0 +1,344 @@
#!/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_lag_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_lag_interfaces
version_added: 2.9
short_description: Manage Link Aggregation on Juniper JUNOS devices.
description: This module manages properties of Link Aggregation Group on Juniper JUNOS devices.
author: Ganesh Nalawade (@ganeshrn)
options:
config:
description: A list of link aggregation group configurations.
type: list
suboptions:
name:
description:
- Name of the link aggregation group (LAG).
type: str
required: True
mode:
description:
- LAG mode. A value of C(passive) will enable LACP in C(passive) mode that is it
will respond to LACP packets and C(active) configures the link to initiate
transmission of LACP packets.
choices: ['active', 'passive']
link_protection:
description:
- This boolean option indicates if link protection should be enabled for the LAG interface.
If value is C(True) link protection is enabled on LAG and if value is C(False) link protection
is disabled.
type: bool
members:
description:
- List of member interfaces of the link aggregation group. The value can be
single interface or list of interfaces.
type: list
suboptions:
member:
description:
- Name of the member interface.
type: str
link_type:
description:
- The value of this options configures the member link as either C(primary)
or C(backup). Value C(primary) configures primary interface for link-protection mode
and C(backup) configures backup interface for link-protection mode.
choices: ['primary', 'backup']
state:
description:
- The state the configuration should be left in
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
requirements:
- ncclient (>=v0.6.4)
notes:
- This module requires the netconf system service be enabled on
the remote device being managed.
- Tested against vSRX JUNOS version 18.4R1.
- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
"""
EXAMPLES = """
# Using merged
# Before state:
# -------------
# user@junos01# show interfaces
# ge-0/0/1 {
# description "Ansible configured interface 1";
# ether-options {
# 802.3ad ae0;
# }
# }
# ge-0/0/2 {
# description "Ansible configured interface 2";
# ether-options {
# 802.3ad ae0;
# }
# }
# ae0 {
# description "lag interface";
# }
# ae1 {
# description "lag interface 1";
# }
- name: "Delete LAG attributes of given interfaces (Note: This won't delete the interface itself)"
junos_lag_interfaces:
config:
- name: ae0
- name: ae1
state: deleted
# After state:
# -------------
# user@junos01# show interfaces
# ge-0/0/1 {
# description "Ansible configured interface 1";
# }
# ge-0/0/2 {
# description "Ansible configured interface 2";
# }
# Using merged
# Before state:
# -------------
# user@junos01# show interfaces
# ge-0/0/1 {
# description "Ansible configured interface 1";
# }
# ge-0/0/2 {
# description "Ansible configured interface 2";
# }
- name: Merge provided configuration with device configuration
junos_lag_interfaces:
config:
- name: ae0
members:
- member: ge-0/0/1
link_type: primary
- member: ge-0/0/2
link_type: backup
state: merged
# After state:
# -------------
# user@junos01# show interfaces
# ge-0/0/1 {
# description "Ansible configured interface 1";
# ether-options {
# 802.3ad {
# ae0;
# primary;
# }
# }
# }
# ge-0/0/2 {
# description "Ansible configured interface 2";
# ether-options {
# 802.3ad {
# ae0;
# backup;
# }
# }
# }
# Using merged
# Before state:
# -------------
# user@junos01# show interfaces
# ge-0/0/1 {
# description "Ansible configured interface 1";
# ether-options {
# 802.3ad ae0;
# }
# }
# ge-0/0/2 {
# description "Ansible configured interface 2";
# ether-options {
# 802.3ad ae0;
# }
# }
# ae0 {
# description "lag interface";
# }
# ae3 {
# description "lag interface 3";
# }
- name: Overrides all device LAG configuration with provided configuration
junos_lag_interfaces:
config:
- name: ae0
members:
- member: ge-0/0/2
- name: ae1
members:
- member: ge-0/0/1
mode: passive
state: overridden
# After state:
# -------------
# user@junos01# show interfaces
# ge-0/0/1 {
# description "Ansible configured interface 1";
# ether-options {
# 802.3ad ae1;
# }
# }
# ge-0/0/2 {
# description "Ansible configured interface 2";
# ether-options {
# 802.3ad ae0;
# }
# }
# ae0 {
# description "lag interface";
# }
# ae1 {
# aggregated-ether-options {
# lacp {
# active;
# }
# }
# }
# Using merged
# Before state:
# -------------
# user@junos01# show interfaces
# ge-0/0/1 {
# description "Ansible configured interface 1";
# }
# ge-0/0/2 {
# description "Ansible configured interface 2";
# }
# ge-0/0/3 {
# description "Ansible configured interface 3";
# }
- name: Replace device LAG configuration with provided configuration
junos_lag_interfaces:
config:
- name: ae0
members:
- member: ge-0/0/1
mode: active
state: replaced
# After state:
# -------------
# user@junos01# show interfaces
# ge-0/0/1 {
# description "Ansible configured interface 1";
# ether-options {
# 802.3ad ae0;
# }
# }
# ge-0/0/2 {
# description "Ansible configured interface 2";
# }
# ae0 {
# aggregated-ether-options {
# lacp {
# active;
# }
# }
# }
# ge-0/0/3 {
# description "Ansible configured interface 3";
# }
"""
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.
xml:
description: The set of xml rpc payload pushed to the remote device.
returned: always
type: list
sample: ['xml 1', 'xml 2', 'xml 3']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.junos.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs
from ansible.module_utils.network.junos.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,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,12 @@
---
- debug:
msg: "Start junos_lag_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"
- debug:
msg: "End junos_lag_interfaces base config ansible_connection={{ ansible_connection }}"

View file

@ -0,0 +1,16 @@
---
- debug:
msg: "Start junos_lag_interfaces teardown ansible_connection={{ ansible_connection }}"
- name: Remove interface config
junos_config:
lines:
- 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 ae1
- delete interfaces ae2
- debug:
msg: "End junos_lag_interfaces teardown ansible_connection={{ ansible_connection }}"

View file

@ -0,0 +1,86 @@
---
- debug:
msg: "START junos_lag_interfaces deleted integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _base_config.yaml
- set_fact:
expected_deleted_1_output:
- name: ae1
members:
- member: ge-0/0/1
- member: ge-0/0/2
mode: active
expected_deleted_2_output: []
- block:
- name: Base LAG configuration
junos_lag_interfaces: &initial
config:
- name: ae1
members:
- member: ge-0/0/1
- member: ge-0/0/2
mode: active
- name: ae2
link_protection: True
members:
- member: ge-0/0/3
link_type: primary
- member: ge-0/0/4
link_type: backup
mode: passive
state: merged
- name: Delete the provided configuration with the exisiting running configuration
junos_lag_interfaces: &deleted
config:
- name: ae2
state: deleted
register: result
- name: Assert the configuration is reflected on host
assert:
that:
- "{{ expected_deleted_1_output | symmetric_difference(result['after']) |length == 0 }}"
- name: Delete the provided configuration with the existing running configuration (IDEMPOTENT)
junos_lag_interfaces: *deleted
register: result
- name: Assert that the previous task was idempotent
assert:
that:
- "result['changed'] == false"
- name: Configure initial state for lag interface
junos_lag_interfaces: *initial
register: result
- name: Delete the provided configuration with the exisiting running configuration
junos_lag_interfaces:
state: deleted
register: result
- name: Assert the configuration is reflected on host
assert:
that:
- "{{ expected_deleted_2_output | symmetric_difference(result['after']) |length == 0 }}"
- name: Delete the provided configuration with the exisiting running configuration (IDEMPOTENT)
junos_lag_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_lag_interfaces deleted integration tests on connection={{ ansible_connection }}"

View file

@ -0,0 +1,62 @@
---
- debug:
msg: "START junos_lag_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
members:
- member: ge-0/0/1
- member: ge-0/0/2
mode: active
- name: ae2
link_protection: True
members:
- member: ge-0/0/3
link_type: primary
- member: ge-0/0/4
link_type: backup
mode: passive
- block:
- name: Merge the provided configuration with the exisiting running configuration
junos_lag_interfaces: &merged
config:
- name: ae1
members:
- member: ge-0/0/1
- member: ge-0/0/2
mode: active
- name: ae2
link_protection: True
members:
- member: ge-0/0/3
link_type: primary
- member: ge-0/0/4
link_type: backup
mode: passive
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_lag_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_lag_interfaces merged integration tests on connection={{ ansible_connection }}"

View file

@ -0,0 +1,62 @@
---
- debug:
msg: "START junos_lag_interfaces overridden integration tests on connection={{ ansible_connection }}"
- include_tasks: _remove_config.yaml
- include_tasks: _base_config.yaml
- set_fact:
expected_overridden_output:
- name: ae1
members:
- member: ge-0/0/2
mode: active
- block:
- name: Base LAG configuration
junos_lag_interfaces:
config:
- name: ae1
members:
- member: ge-0/0/1
- member: ge-0/0/2
mode: active
- name: ae2
link_protection: True
members:
- member: ge-0/0/3
link_type: primary
- member: ge-0/0/4
link_type: backup
mode: passive
state: merged
- name: Override the provided configuration with the exisiting running configuration
junos_lag_interfaces: &overridden
config:
- name: ae1
members:
- member: ge-0/0/2
mode: active
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_lag_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_lag_interfaces overridden integration tests on connection={{ ansible_connection }}"

View file

@ -0,0 +1,70 @@
---
- debug:
msg: "START junos_lag_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
members:
- member: ge-0/0/1
mode: passive
- name: ae2
link_protection: True
members:
- member: ge-0/0/3
link_type: primary
- member: ge-0/0/4
link_type: backup
mode: passive
- block:
- name: Base LAG configuration
junos_lag_interfaces:
config:
- name: ae1
members:
- member: ge-0/0/1
- member: ge-0/0/2
mode: active
- name: ae2
link_protection: True
members:
- member: ge-0/0/3
link_type: primary
- member: ge-0/0/4
link_type: backup
mode: passive
state: merged
- name: Replace the provided configuration with the exisiting running configuration
junos_lag_interfaces: &replaced
config:
- name: ae1
members:
- member: ge-0/0/1
mode: passive
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_lag_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_lag_interfaces replaced integration tests on connection={{ ansible_connection }}"

View file

@ -2480,9 +2480,9 @@ lib/ansible/modules/network/junos/junos_l2_interface.py E337
lib/ansible/modules/network/junos/junos_l2_interface.py E338
lib/ansible/modules/network/junos/junos_l3_interface.py E337
lib/ansible/modules/network/junos/junos_l3_interface.py E338
lib/ansible/modules/network/junos/junos_linkagg.py E324
lib/ansible/modules/network/junos/junos_linkagg.py E337
lib/ansible/modules/network/junos/junos_linkagg.py E338
lib/ansible/modules/network/junos/_junos_linkagg.py E324
lib/ansible/modules/network/junos/_junos_linkagg.py E337
lib/ansible/modules/network/junos/_junos_linkagg.py E338
lib/ansible/modules/network/junos/junos_lldp_interface.py E338
lib/ansible/modules/network/junos/junos_lldp.py E337
lib/ansible/modules/network/junos/junos_lldp.py E338