Add aggregate for junos modules and sub spec validation (#27726)

* Add aggregate for junos modules and sub spec validation

*  aggregate support of junos modules
*  aggregate sub spec validation
*  relevant changes to junos integration test
*  junos module boilerplate changes

* Add new boilerplate for junos modules

* Fix CI issues
pull/27790/head
Ganesh Nalawade 7 years ago committed by GitHub
parent 6d59ac1bb4
commit d3e5d30f7c

@ -184,6 +184,10 @@ def locked_config(module):
def get_diff(module): def get_diff(module):
reply = get_configuration(module, compare=True, format='text') reply = get_configuration(module, compare=True, format='text')
# if warning is received from device diff is empty.
if isinstance(reply, list):
return None
output = reply.find('.//configuration-output') output = reply.find('.//configuration-output')
if output is not None: if output is not None:
return to_text(output.text, encoding='latin1').strip() return to_text(output.text, encoding='latin1').strip()
@ -210,7 +214,7 @@ def get_param(module, key):
return module.params[key] or module.params['provider'].get(key) return module.params[key] or module.params['provider'].get(key)
def map_params_to_obj(module, param_to_xpath_map): def map_params_to_obj(module, param_to_xpath_map, param=None):
""" """
Creates a new dictionary with key as xpath corresponding Creates a new dictionary with key as xpath corresponding
to param and value is a list of dict with metadata and values for to param and value is a list of dict with metadata and values for
@ -231,12 +235,15 @@ def map_params_to_obj(module, param_to_xpath_map):
:param param_to_xpath_map: Modules params to xpath map :param param_to_xpath_map: Modules params to xpath map
:return: obj :return: obj
""" """
if not param:
param = module.params
obj = collections.OrderedDict() obj = collections.OrderedDict()
for key, attribute in param_to_xpath_map.items(): for key, attribute in param_to_xpath_map.items():
if key in module.params: if key in param:
is_attribute_dict = False is_attribute_dict = False
value = module.params[key] value = param[key]
if not isinstance(value, (list, tuple)): if not isinstance(value, (list, tuple)):
value = [value] value = [value]
@ -261,10 +268,13 @@ def map_params_to_obj(module, param_to_xpath_map):
return obj return obj
def map_obj_to_ele(module, want, top, value_map=None): def map_obj_to_ele(module, want, top, value_map=None, param=None):
if not HAS_LXML: if not HAS_LXML:
module.fail_json(msg='lxml is not installed.') module.fail_json(msg='lxml is not installed.')
if not param:
param = module.params
root = Element('root') root = Element('root')
top_ele = top.split('/') top_ele = top.split('/')
ele = SubElement(root, top_ele[0]) ele = SubElement(root, top_ele[0])
@ -273,8 +283,8 @@ def map_obj_to_ele(module, want, top, value_map=None):
for item in top_ele[1:-1]: for item in top_ele[1:-1]:
ele = SubElement(ele, item) ele = SubElement(ele, item)
container = ele container = ele
state = module.params.get('state') state = param.get('state')
active = module.params.get('active') active = param.get('active')
if active: if active:
oper = 'active' oper = 'active'
else: else:
@ -374,3 +384,14 @@ def map_obj_to_ele(module, want, top, value_map=None):
par.set('delete', 'delete') par.set('delete', 'delete')
return root.getchildren()[0] return root.getchildren()[0]
def to_param_list(module):
aggregate = module.params.get('aggregate')
if aggregate:
if isinstance(aggregate, dict):
return [aggregate]
else:
return aggregate
else:
return [module.params]

@ -2,26 +2,15 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc # (c) 2017, Ansible by Red Hat, inc
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This file is part of Ansible by Red Hat
# from __future__ import absolute_import, division, print_function
# Ansible is free software: you can redistribute it and/or modify __metaclass__ = type
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'core'}
DOCUMENTATION = """ DOCUMENTATION = """
--- ---

@ -1,20 +1,12 @@
#!/usr/bin/python #!/usr/bin/python
# # -*- coding: utf-8 -*-
# This file is part of Ansible
# # (c) 2017, Ansible by Red Hat, inc
# Ansible is free software: you can redistribute it and/or modify # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or from __future__ import absolute_import, division, print_function
# (at your option) any later version. __metaclass__ = type
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],

@ -1,20 +1,12 @@
#!/usr/bin/python #!/usr/bin/python
# # -*- coding: utf-8 -*-
# This file is part of Ansible
# # (c) 2017, Ansible by Red Hat, inc
# Ansible is free software: you can redistribute it and/or modify # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or from __future__ import absolute_import, division, print_function
# (at your option) any later version. __metaclass__ = type
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],

@ -1,20 +1,12 @@
#!/usr/bin/python #!/usr/bin/python
# # -*- coding: utf-8 -*-
# This file is part of Ansible
# # (c) 2017, Ansible by Red Hat, inc
# Ansible is free software: you can redistribute it and/or modify # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or from __future__ import absolute_import, division, print_function
# (at your option) any later version. __metaclass__ = type
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],

@ -2,22 +2,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc # (c) 2017, Ansible by Red Hat, inc
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This file is part of Ansible by Red Hat
# from __future__ import absolute_import, division, print_function
# Ansible is free software: you can redistribute it and/or modify __metaclass__ = type
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],
@ -65,7 +54,7 @@ options:
description: List of Interfaces definitions. description: List of Interfaces definitions.
purge: purge:
description: description:
- Purge Interfaces not defined in the aggregates parameter. - Purge Interfaces not defined in the aggregate parameter.
This applies only for logical interface. This applies only for logical interface.
default: no default: no
state: state:
@ -126,6 +115,18 @@ EXAMPLES = """
speed: 1g speed: 1g
mtu: 256 mtu: 256
duplex: full duplex: full
- name: Create interface using aggregate
junos_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256}
- name: Delete interface using aggregate
junos_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, state: absent}
- { name: ge-0/0/2, description: test-interface-2, state: absent}
""" """
RETURN = """ RETURN = """
@ -141,10 +142,12 @@ diff.prepared:
""" """
import collections import collections
from copy import copy
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.junos import junos_argument_spec, check_args from ansible.module_utils.junos import junos_argument_spec, check_args
from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele
from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config, to_param_list
try: try:
from lxml.etree import tostring from lxml.etree import tostring
@ -159,19 +162,21 @@ def validate_mtu(value, module):
module.fail_json(msg='mtu must be between 256 and 9192') module.fail_json(msg='mtu must be between 256 and 9192')
def validate_param_values(module, obj): def validate_param_values(module, obj, param=None):
if not param:
param = module.params
for key in obj: for key in obj:
# validate the param value (if validator func exists) # validate the param value (if validator func exists)
validator = globals().get('validate_%s' % key) validator = globals().get('validate_%s' % key)
if callable(validator): if callable(validator):
validator(module.params.get(key), module) validator(param.get(key), module)
def main(): def main():
""" main entry point for module execution """ main entry point for module execution
""" """
argument_spec = dict( element_spec = dict(
name=dict(required=True), name=dict(),
description=dict(), description=dict(),
enabled=dict(), enabled=dict(),
speed=dict(), speed=dict(),
@ -179,16 +184,30 @@ def main():
duplex=dict(choices=['full', 'half', 'auto']), duplex=dict(choices=['full', 'half', 'auto']),
tx_rate=dict(), tx_rate=dict(),
rx_rate=dict(), rx_rate=dict(),
aggregate=dict(),
purge=dict(default=False, type='bool'),
state=dict(default='present', state=dict(default='present',
choices=['present', 'absent', 'up', 'down']), choices=['present', 'absent', 'up', 'down']),
active=dict(default=True, type='bool') active=dict(default=True, type='bool')
) )
aggregate_spec = copy(element_spec)
aggregate_spec['name'] = dict(required=True)
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
purge=dict(default=False, type='bool')
)
argument_spec.update(element_spec)
argument_spec.update(junos_argument_spec) argument_spec.update(junos_argument_spec)
required_one_of = [['name', 'aggregate']]
mutually_exclusive = [['name', 'aggregate'],
['state', 'aggregate'],
['active', 'aggregate']]
module = AnsibleModule(argument_spec=argument_spec, module = AnsibleModule(argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True) supports_check_mode=True)
warnings = list() warnings = list()
@ -211,26 +230,33 @@ def main():
('disable', {'xpath': 'disable', 'tag_only': True}) ('disable', {'xpath': 'disable', 'tag_only': True})
]) ])
state = module.params.get('state')
module.params['disable'] = True if state == 'down' else False
if state in ('present', 'up', 'down'):
module.params['state'] = 'present'
else:
module.params['disable'] = True
choice_to_value_map = { choice_to_value_map = {
'link-mode': {'full': 'full-duplex', 'half': 'half-duplex', 'auto': 'automatic'} 'link-mode': {'full': 'full-duplex', 'half': 'half-duplex', 'auto': 'automatic'}
} }
validate_param_values(module, param_to_xpath_map) params = to_param_list(module)
requests = list()
for param in params:
item = copy(param)
state = item.get('state')
item['disable'] = True if state == 'down' else False
if state in ('present', 'up', 'down'):
item['state'] = 'present'
else:
item['disable'] = True
want = map_params_to_obj(module, param_to_xpath_map) validate_param_values(module, param_to_xpath_map, param=item)
ele = map_obj_to_ele(module, want, top, choice_to_value_map) want = map_params_to_obj(module, param_to_xpath_map, param=item)
requests.append(map_obj_to_ele(module, want, top, value_map=choice_to_value_map, param=item))
diff = None
with locked_config(module): with locked_config(module):
diff = load_config(module, tostring(ele), warnings, action='replace') for req in requests:
diff = load_config(module, tostring(req), warnings, action='replace')
# issue commit after last configuration change is done
commit = not module.check_mode commit = not module.check_mode
if diff: if diff:
if commit: if commit:

@ -2,22 +2,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc # (c) 2017, Ansible by Red Hat, inc
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This file is part of Ansible by Red Hat
# from __future__ import absolute_import, division, print_function
# Ansible is free software: you can redistribute it and/or modify __metaclass__ = type
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],
@ -80,6 +69,25 @@ EXAMPLES = """
junos_l3_interface: junos_l3_interface:
name: ge-0/0/1 name: ge-0/0/1
state: absent state: absent
- name: Set ipv4 address using aggregate
junos_l3_interface:
aggregate:
- name: ge-0/0/1
ipv4: 1.1.1.1
- name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
- name: Delete ipv4 address using aggregate
junos_l3_interface:
aggregate:
- name: ge-0/0/1
ipv4: 1.1.1.1
state: absent
- name: ge-0/0/2
ipv4: 2.2.2.2
state: absent
""" """
RETURN = """ RETURN = """
@ -95,10 +103,12 @@ diff:
""" """
import collections import collections
from copy import copy
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.junos import junos_argument_spec, check_args from ansible.module_utils.junos import junos_argument_spec, check_args
from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele
from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config, to_param_list
try: try:
from lxml.etree import tostring from lxml.etree import tostring
@ -111,23 +121,35 @@ USE_PERSISTENT_CONNECTION = True
def main(): def main():
""" main entry point for module execution """ main entry point for module execution
""" """
argument_spec = dict( element_spec = dict(
name=dict(required=True), name=dict(),
ipv4=dict(), ipv4=dict(),
ipv6=dict(), ipv6=dict(),
unit=dict(default=0, type='int'), unit=dict(default=0, type='int'),
aggregate=dict(),
purge=dict(default=False, type='bool'),
state=dict(default='present', choices=['present', 'absent']), state=dict(default='present', choices=['present', 'absent']),
active=dict(default=True, type='bool') active=dict(default=True, type='bool')
) )
aggregate_spec = copy(element_spec)
aggregate_spec['name'] = dict(required=True)
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
purge=dict(default=False, type='bool')
)
argument_spec.update(element_spec)
argument_spec.update(junos_argument_spec) argument_spec.update(junos_argument_spec)
required_one_of = [['ipv4', 'ipv6']] required_one_of = [['name', 'aggregate']]
mutually_exclusive = [['name', 'aggregate'],
['state', 'aggregate'],
['active', 'aggregate']]
module = AnsibleModule(argument_spec=argument_spec, module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True, supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
required_one_of=required_one_of) required_one_of=required_one_of)
warnings = list() warnings = list()
@ -148,11 +170,21 @@ def main():
('ipv6', {'xpath': 'inet6/address/name', 'top': 'unit/family', 'is_key': True}) ('ipv6', {'xpath': 'inet6/address/name', 'top': 'unit/family', 'is_key': True})
]) ])
want = map_params_to_obj(module, param_to_xpath_map) params = to_param_list(module)
ele = map_obj_to_ele(module, want, top)
requests = list()
for param in params:
item = copy(param)
if not item['ipv4'] and not item['ipv6']:
module.fail_json(msg="one of the following is required: ipv4,ipv6")
want = map_params_to_obj(module, param_to_xpath_map, param=item)
requests.append(map_obj_to_ele(module, want, top, param=item))
diff = None
with locked_config(module): with locked_config(module):
diff = load_config(module, tostring(ele), warnings, action='replace') for req in requests:
diff = load_config(module, tostring(req), warnings, action='replace')
commit = not module.check_mode commit = not module.check_mode
if diff: if diff:

@ -2,22 +2,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc # (c) 2017, Ansible by Red Hat, inc
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This file is part of Ansible by Red Hat
# from __future__ import absolute_import, division, print_function
# Ansible is free software: you can redistribute it and/or modify __metaclass__ = type
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],
@ -62,12 +51,6 @@ options:
description: description:
description: description:
- Description of Interface. - Description of Interface.
collection:
description: List of link aggregation definitions.
purge:
description:
- Purge link aggregation groups not defined in the collections parameter.
default: no
state: state:
description: description:
- State of the link aggregation group. - State of the link aggregation group.
@ -281,8 +264,6 @@ def main():
min_links=dict(type='int'), min_links=dict(type='int'),
device_count=dict(type='int'), device_count=dict(type='int'),
description=dict(default=DEFAULT_COMMENT), description=dict(default=DEFAULT_COMMENT),
collection=dict(type='list'),
purge=dict(type='bool'),
state=dict(default='present', choices=['present', 'absent', 'up', 'down']), state=dict(default='present', choices=['present', 'absent', 'up', 'down']),
active=dict(default=True, type='bool') active=dict(default=True, type='bool')
) )

@ -2,22 +2,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc # (c) 2017, Ansible by Red Hat, inc
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This file is part of Ansible by Red Hat
# from __future__ import absolute_import, division, print_function
# Ansible is free software: you can redistribute it and/or modify __metaclass__ = type
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],

@ -2,22 +2,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc # (c) 2017, Ansible by Red Hat, inc
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This file is part of Ansible by Red Hat
# from __future__ import absolute_import, division, print_function
# Ansible is free software: you can redistribute it and/or modify __metaclass__ = type
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],

@ -2,22 +2,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc # (c) 2017, Ansible by Red Hat, inc
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This file is part of Ansible by Red Hat
# from __future__ import absolute_import, division, print_function
# Ansible is free software: you can redistribute it and/or modify __metaclass__ = type
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],
@ -53,7 +42,7 @@ options:
description: List of logging definitions. description: List of logging definitions.
purge: purge:
description: description:
- Purge logging not defined in the aggregates parameter. - Purge logging not defined in the aggregate parameter.
default: no default: no
state: state:
description: description:
@ -108,6 +97,18 @@ EXAMPLES = """
files: 30 files: 30
size: 65536 size: 65536
rotate_frequency: 10 rotate_frequency: 10
- name: Configure file logging using aggregate
junos_logging:
aggregate:
- {dest: file, name: test-1, facility: pfe, level: critical, active: True}
- {dest: file, name: test-2, facility: kernel, level: emergency, active: True}
- name: Delete file logging using aggregate
junos_logging:
aggregate:
- {dest: file, name: test-1, facility: pfe, level: critical, active: True, state: absent}
- {dest: file, name: test-2, facility: kernel, level: emergency, active: True, state: absent}
""" """
RETURN = """ RETURN = """
@ -125,9 +126,11 @@ diff.prepared:
""" """
import collections import collections
from copy import copy
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.junos import junos_argument_spec, check_args from ansible.module_utils.junos import junos_argument_spec, check_args
from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele, to_param_list
from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config
try: try:
@ -153,18 +156,20 @@ def validate_rotate_frequency(value, module):
module.fail_json(msg='rotate_frequency must be between 1 and 59') module.fail_json(msg='rotate_frequency must be between 1 and 59')
def validate_param_values(module, obj): def validate_param_values(module, obj, param=None):
if not param:
param = module.params
for key in obj: for key in obj:
# validate the param value (if validator func exists) # validate the param value (if validator func exists)
validator = globals().get('validate_%s' % key) validator = globals().get('validate_%s' % key)
if callable(validator): if callable(validator):
validator(module.params.get(key), module) validator(param.get(key), module)
def main(): def main():
""" main entry point for module execution """ main entry point for module execution
""" """
argument_spec = dict( element_spec = dict(
dest=dict(choices=['console', 'host', 'file', 'user']), dest=dict(choices=['console', 'host', 'file', 'user']),
name=dict(), name=dict(),
facility=dict(), facility=dict(),
@ -173,14 +178,28 @@ def main():
size=dict(type='int'), size=dict(type='int'),
files=dict(type='int'), files=dict(type='int'),
src_addr=dict(), src_addr=dict(),
aggregate=dict(),
purge=dict(default=False, type='bool'),
state=dict(default='present', choices=['present', 'absent']), state=dict(default='present', choices=['present', 'absent']),
active=dict(default=True, type='bool') active=dict(default=True, type='bool')
) )
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=element_spec),
purge=dict(default=False, type='bool')
)
argument_spec.update(element_spec)
argument_spec.update(junos_argument_spec) argument_spec.update(junos_argument_spec)
mutually_exclusive = [['dest', 'aggregate'],
['name', 'aggregate'],
['facility', 'aggregate'],
['rotate_frequency', 'aggregate'],
['size', 'aggregate'],
['files', 'aggregate'],
['src_addr', 'aggregate'],
['state', 'aggregate'],
['active', 'aggregate']]
required_if = [('dest', 'host', ['name', 'facility', 'level']), required_if = [('dest', 'host', ['name', 'facility', 'level']),
('dest', 'file', ['name', 'facility', 'level']), ('dest', 'file', ['name', 'facility', 'level']),
('dest', 'user', ['name', 'facility', 'level']), ('dest', 'user', ['name', 'facility', 'level']),
@ -188,6 +207,7 @@ def main():
module = AnsibleModule(argument_spec=argument_spec, module = AnsibleModule(argument_spec=argument_spec,
required_if=required_if, required_if=required_if,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True) supports_check_mode=True)
warnings = list() warnings = list()
@ -198,8 +218,13 @@ def main():
if warnings: if warnings:
result['warnings'] = warnings result['warnings'] = warnings
dest = module.params.get('dest') params = to_param_list(module)
if dest == 'console' and module.params.get('name'):
requests = list()
for param in params:
item = copy(param)
dest = item.get('dest')
if dest == 'console' and item.get('name'):
module.fail_json(msg="%s and %s are mutually exclusive" % ('console', 'name')) module.fail_json(msg="%s and %s are mutually exclusive" % ('console', 'name'))
top = 'system/syslog' top = 'system/syslog'
@ -222,16 +247,18 @@ def main():
('rotate_frequency', {'xpath': 'log-rotate-frequency', 'leaf_only': True}), ('rotate_frequency', {'xpath': 'log-rotate-frequency', 'leaf_only': True}),
]) ])
if module.params.get('level'): if item.get('level'):
param_to_xpath_map['level'] = {'xpath': module.params.get('level'), 'tag_only': True, 'top': field_top} param_to_xpath_map['level'] = {'xpath': item.get('level'), 'tag_only': True, 'top': field_top}
validate_param_values(module, param_to_xpath_map) validate_param_values(module, param_to_xpath_map, param=item)
want = map_params_to_obj(module, param_to_xpath_map) want = map_params_to_obj(module, param_to_xpath_map, param=item)
ele = map_obj_to_ele(module, want, top) requests.append(map_obj_to_ele(module, want, top, param=item))
diff = None
with locked_config(module): with locked_config(module):
diff = load_config(module, tostring(ele), warnings, action='replace') for req in requests:
diff = load_config(module, tostring(req), warnings, action='replace')
commit = not module.check_mode commit = not module.check_mode
if diff: if diff:

@ -1,20 +1,12 @@
#!/usr/bin/python #!/usr/bin/python
# # -*- coding: utf-8 -*-
# This file is part of Ansible
# # (c) 2017, Ansible by Red Hat, inc
# Ansible is free software: you can redistribute it and/or modify # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or from __future__ import absolute_import, division, print_function
# (at your option) any later version. __metaclass__ = type
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],

@ -1,24 +1,16 @@
#!/usr/bin/python #!/usr/bin/python
# # -*- coding: utf-8 -*-
# This file is part of Ansible
# # (c) 2017, Ansible by Red Hat, inc
# Ansible is free software: you can redistribute it and/or modify # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or from __future__ import absolute_import, division, print_function
# (at your option) any later version. __metaclass__ = type
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'core'}
DOCUMENTATION = """ DOCUMENTATION = """

@ -1,20 +1,12 @@
#!/usr/bin/python #!/usr/bin/python
# # -*- coding: utf-8 -*-
# This file is part of Ansible
# # (c) 2017, Ansible by Red Hat, inc
# Ansible is free software: you can redistribute it and/or modify # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or from __future__ import absolute_import, division, print_function
# (at your option) any later version. __metaclass__ = type
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],

@ -2,22 +2,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc # (c) 2017, Ansible by Red Hat, inc
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This file is part of Ansible by Red Hat
# from __future__ import absolute_import, division, print_function
# Ansible is free software: you can redistribute it and/or modify __metaclass__ = type
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],
@ -58,7 +47,7 @@ options:
description: List of static route definitions description: List of static route definitions
purge: purge:
description: description:
- Purge static routes not defined in the aggregates parameter. - Purge static routes not defined in the aggregate parameter.
default: no default: no
state: state:
description: description:
@ -111,6 +100,18 @@ EXAMPLES = """
qualified_preference: 3 qualified_preference: 3
state: present state: present
active: True active: True
- name: Configure static route using aggregate
junos_static_route:
aggregate:
- {address: 4.4.4.0/24, next_hop: 3.3.3.3, preference: 10, qualified_next_hop: 5.5.5.5, qualified_preference: 30}
- {address: 5.5.5.0/24, next_hop: 6.6.6.6, preference: 11, qualified_next_hop: 7.7.7.7, qualified_preference: 12}
- name: Delete static route using aggregate
junos_static_route:
aggregate:
- {address: 4.4.4.0/24, state: absent}
- {address: 5.5.5.0/24, state: absent}
""" """
RETURN = """ RETURN = """
@ -131,9 +132,11 @@ diff.prepared:
""" """
import collections import collections
from copy import copy
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.junos import junos_argument_spec, check_args from ansible.module_utils.junos import junos_argument_spec, check_args
from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele, to_param_list
from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config
try: try:
@ -144,32 +147,38 @@ except ImportError:
USE_PERSISTENT_CONNECTION = True USE_PERSISTENT_CONNECTION = True
def validate_param_values(module, obj):
for key in obj:
# validate the param value (if validator func exists)
validator = globals().get('validate_%s' % key)
if callable(validator):
validator(module.params.get(key), module)
def main(): def main():
""" main entry point for module execution """ main entry point for module execution
""" """
argument_spec = dict( element_spec = dict(
address=dict(required=True, type='str', aliases=['prefix']), address=dict(type='str', aliases=['prefix']),
next_hop=dict(type='str'), next_hop=dict(type='str'),
preference=dict(type='int', aliases=['admin_distance']), preference=dict(type='int', aliases=['admin_distance']),
qualified_next_hop=dict(type='str'), qualified_next_hop=dict(type='str'),
qualified_preference=dict(type='int'), qualified_preference=dict(type='int'),
aggregate=dict(type='list'),
purge=dict(type='bool'),
state=dict(default='present', choices=['present', 'absent']), state=dict(default='present', choices=['present', 'absent']),
active=dict(default=True, type='bool') active=dict(default=True, type='bool')
) )
aggregate_spec = copy(element_spec)
aggregate_spec['address'] = dict(required=True)
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
purge=dict(default=False, type='bool')
)
argument_spec.update(element_spec)
argument_spec.update(junos_argument_spec) argument_spec.update(junos_argument_spec)
required_one_of = [['aggregate', 'address']] required_one_of = [['aggregate', 'address']]
mutually_exclusive = [['aggregate', 'address']] mutually_exclusive = [['aggregate', 'address'],
['aggregate', 'next_hop'],
['aggregate', 'preference'],
['aggregate', 'qualified_next_hop'],
['aggregate', 'qualified_preference'],
['aggregate', 'state'],
['aggregate', 'active']]
module = AnsibleModule(argument_spec=argument_spec, module = AnsibleModule(argument_spec=argument_spec,
required_one_of=required_one_of, required_one_of=required_one_of,
@ -179,10 +188,6 @@ def main():
warnings = list() warnings = list()
check_args(module, warnings) check_args(module, warnings)
if module.params['state'] == 'present':
if not module.params['address'] and module.params['next_hop']:
module.fail_json(msg="parameters are required together: ['address', 'next_hop']")
result = {'changed': False} result = {'changed': False}
if warnings: if warnings:
@ -199,13 +204,21 @@ def main():
('qualified_preference', {'xpath': 'preference', 'top': 'qualified-next-hop'}) ('qualified_preference', {'xpath': 'preference', 'top': 'qualified-next-hop'})
]) ])
validate_param_values(module, param_to_xpath_map) params = to_param_list(module)
requests = list()
for param in params:
item = copy(param)
if item['state'] == 'present':
if not item['address'] and item['next_hop']:
module.fail_json(msg="parameters are required together: ['address', 'next_hop']")
want = map_params_to_obj(module, param_to_xpath_map) want = map_params_to_obj(module, param_to_xpath_map, param=item)
ele = map_obj_to_ele(module, want, top) requests.append(map_obj_to_ele(module, want, top, param=item))
with locked_config(module): with locked_config(module):
diff = load_config(module, tostring(ele), warnings, action='replace') for req in requests:
diff = load_config(module, tostring(req), warnings, action='replace')
commit = not module.check_mode commit = not module.check_mode
if diff: if diff:

@ -2,22 +2,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc # (c) 2017, Ansible by Red Hat, inc
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This file is part of Ansible by Red Hat
# from __future__ import absolute_import, division, print_function
# Ansible is free software: you can redistribute it and/or modify __metaclass__ = type
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],

@ -1,20 +1,12 @@
#!/usr/bin/python #!/usr/bin/python
# # -*- coding: utf-8 -*-
# This file is part of Ansible
# # (c) 2017, Ansible by Red Hat, inc
# Ansible is free software: you can redistribute it and/or modify # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or from __future__ import absolute_import, division, print_function
# (at your option) any later version. __metaclass__ = type
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],
@ -122,6 +114,18 @@ EXAMPLES = """
junos_user: junos_user:
name: ansible name: ansible
purge: yes purge: yes
- name: Create list of users
junos_user:
aggregate:
- {name: test_user1, full_name: test_user2, role: operator, state: present}
- {name: test_user2, full_name: test_user2, role: read-only, state: present}
- name: Delete list of users
junos_user:
aggregate:
- {name: test_user1, full_name: test_user2, role: operator, state: absent}
- {name: test_user2, full_name: test_user2, role: read-only, state: absent}
""" """
RETURN = """ RETURN = """
@ -251,26 +255,35 @@ def map_params_to_obj(module):
def main(): def main():
""" main entry point for module execution """ main entry point for module execution
""" """
argument_spec = dict( element_spec = dict(
aggregate=dict(type='list', aliases=['collection', 'users']),
name=dict(), name=dict(),
full_name=dict(), full_name=dict(),
role=dict(choices=ROLES, default='unauthorized'), role=dict(choices=ROLES, default='unauthorized'),
sshkey=dict(), sshkey=dict(),
purge=dict(type='bool'),
state=dict(choices=['present', 'absent'], default='present'), state=dict(choices=['present', 'absent'], default='present'),
active=dict(default=True, type='bool') active=dict(default=True, type='bool')
) )
mutually_exclusive = [('aggregate', 'name')] argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=element_spec, aliases=['collection', 'users']),
purge=dict(default=False, type='bool')
)
argument_spec.update(element_spec)
argument_spec.update(junos_argument_spec)
required_one_of = [['aggregate', 'name']]
mutually_exclusive = [['aggregate', 'name'],
['aggregate', 'full_name'],
['aggregate', 'sshkey'],
['aggregate', 'state'],
['aggregate', 'active']]
argument_spec.update(junos_argument_spec) argument_spec.update(junos_argument_spec)
module = AnsibleModule(argument_spec=argument_spec, module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive, mutually_exclusive=mutually_exclusive,
required_one_of=required_one_of,
supports_check_mode=True) supports_check_mode=True)
warnings = list() warnings = list()

@ -2,28 +2,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc # (c) 2017, Ansible by Red Hat, inc
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This file is part of Ansible by Red Hat
# from __future__ import absolute_import, division, print_function
# Ansible is free software: you can redistribute it and/or modify __metaclass__ = type
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'core'} 'supported_by': 'core'}
DOCUMENTATION = """ DOCUMENTATION = """
--- ---
module: junos_vlan module: junos_vlan
@ -53,7 +41,7 @@ options:
description: List of VLANs definitions. description: List of VLANs definitions.
purge: purge:
description: description:
- Purge VLANs not defined in the aggregates parameter. - Purge VLANs not defined in the aggregate parameter.
default: no default: no
state: state:
description: description:
@ -95,6 +83,18 @@ EXAMPLES = """
vlan_name: test vlan_name: test
state: present state: present
active: True active: True
- name: Create vlan configuration using aggregate
junos_vlan:
aggregate:
- { vlan_id: 159, name: test_vlan_1, description: test vlan-1, state: present }
- { vlan_id: 160, name: test_vlan_2, description: test vlan-2, state: present }
- name: Delete vlan configuration using aggregate
junos_vlan:
aggregate:
- { vlan_id: 159, name: test_vlan_1, state: absent }
- { vlan_id: 160, name: test_vlan_2, state: absent }
""" """
RETURN = """ RETURN = """
@ -110,9 +110,11 @@ diff.prepared:
""" """
import collections import collections
from copy import copy
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.junos import junos_argument_spec, check_args from ansible.module_utils.junos import junos_argument_spec, check_args
from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele, to_param_list
from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config
try: try:
@ -128,31 +130,52 @@ def validate_vlan_id(value, module):
module.fail_json(msg='vlan_id must be between 1 and 4094') module.fail_json(msg='vlan_id must be between 1 and 4094')
def validate_param_values(module, obj): def validate_param_values(module, obj, param=None):
if not param:
param = module.params
for key in obj: for key in obj:
# validate the param value (if validator func exists) # validate the param value (if validator func exists)
validator = globals().get('validate_%s' % key) validator = globals().get('validate_%s' % key)
if callable(validator): if callable(validator):
validator(module.params.get(key), module) validator(param.get(key), module)
def main(): def main():
""" main entry point for module execution """ main entry point for module execution
""" """
argument_spec = dict( element_spec = dict(
name=dict(required=True), name=dict(),
vlan_id=dict(required=True, type='int'), vlan_id=dict(type='int'),
description=dict(), description=dict(),
interfaces=dict(), interfaces=dict(),
aggregate=dict(),
purge=dict(default=False, type='bool'),
state=dict(default='present', choices=['present', 'absent']), state=dict(default='present', choices=['present', 'absent']),
active=dict(default=True, type='bool') active=dict(default=True, type='bool')
) )
aggregate_spec = copy(element_spec)
aggregate_spec['name'] = dict(required=True)
aggregate_spec['vlan_id'] = dict(required=True, type='int')
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
purge=dict(default=False, type='bool')
)
argument_spec.update(element_spec)
argument_spec.update(junos_argument_spec) argument_spec.update(junos_argument_spec)
required_one_of = [['aggregate', 'name']]
required_together = [['name', 'vlan_id']]
mutually_exclusive = [['aggregate', 'name'],
['aggregate', 'vlan_id'],
['aggregate', 'description'],
['aggregate', 'interfaces'],
['aggregate', 'state'],
['aggregate', 'active']]
module = AnsibleModule(argument_spec=argument_spec, module = AnsibleModule(argument_spec=argument_spec,
required_one_of=required_one_of,
required_together=required_together,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True) supports_check_mode=True)
warnings = list() warnings = list()
@ -172,13 +195,19 @@ def main():
('description', 'description') ('description', 'description')
]) ])
validate_param_values(module, param_to_xpath_map) params = to_param_list(module)
requests = list()
for param in params:
item = copy(param)
validate_param_values(module, param_to_xpath_map, param=item)
want = map_params_to_obj(module, param_to_xpath_map) want = map_params_to_obj(module, param_to_xpath_map, param=item)
ele = map_obj_to_ele(module, want, top) requests.append(map_obj_to_ele(module, want, top, param=item))
with locked_config(module): with locked_config(module):
diff = load_config(module, tostring(ele), warnings, action='replace') for req in requests:
diff = load_config(module, tostring(req), warnings, action='replace')
commit = not module.check_mode commit = not module.check_mode
if diff: if diff:

@ -2,22 +2,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc # (c) 2017, Ansible by Red Hat, inc
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This file is part of Ansible by Red Hat
# from __future__ import absolute_import, division, print_function
# Ansible is free software: you can redistribute it and/or modify __metaclass__ = type
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0', ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'], 'status': ['preview'],
@ -68,9 +57,8 @@ options:
exclusive with the C(name) argument. exclusive with the C(name) argument.
purge: purge:
description: description:
- Instructs the module to consider the - Instructs the module to consider the VRF definition absolute.
VRF definition absolute. It will remove any previously configured It will remove any previously configured VRFs on the device.
VRFs on the device.
default: false default: false
state: state:
description: description:
@ -136,6 +124,26 @@ EXAMPLES = """
rd: 1.1.1.1:10 rd: 1.1.1.1:10
target: target:65514:113 target: target:65514:113
active: True active: True
- name: Create vrf using aggregate
junos_vrf:
aggregate:
- name: test-1
description: test-vrf-1
interfaces:
- ge-0/0/3
- ge-0/0/2
rd: 1.1.1.1:10
target: target:65514:113
state: present
- name: test-2
description: test-vrf-2
interfaces:
- ge-0/0/4
- ge-0/0/5
rd: 2.2.2.2:10
target: target:65515:114
state: present
""" """
RETURN = """ RETURN = """
@ -156,9 +164,11 @@ diff.prepared:
""" """
import collections import collections
from copy import copy
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.junos import junos_argument_spec, check_args from ansible.module_utils.junos import junos_argument_spec, check_args
from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele from ansible.module_utils.junos import load_config, map_params_to_obj, map_obj_to_ele, to_param_list
from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config from ansible.module_utils.junos import commit_configuration, discard_changes, locked_config
try: try:
@ -172,22 +182,40 @@ USE_PERSISTENT_CONNECTION = True
def main(): def main():
""" main entry point for module execution """ main entry point for module execution
""" """
argument_spec = dict( element_spec = dict(
name=dict(required=True), name=dict(),
description=dict(), description=dict(),
rd=dict(type='list'), rd=dict(type='list'),
interfaces=dict(type='list'), interfaces=dict(type='list'),
target=dict(type='list'), target=dict(type='list'),
aggregate=dict(type='list'),
purge=dict(default=False, type='bool'),
state=dict(default='present', choices=['present', 'absent']), state=dict(default='present', choices=['present', 'absent']),
active=dict(default=True, type='bool') active=dict(default=True, type='bool')
) )
aggregate_spec = copy(element_spec)
aggregate_spec['name'] = dict(required=True)
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
purge=dict(default=False, type='bool')
)
argument_spec.update(element_spec)
argument_spec.update(junos_argument_spec) argument_spec.update(junos_argument_spec)
required_one_of = [['aggregate', 'name']]
mutually_exclusive = [['aggregate', 'name'],
['aggregate', 'description'],
['aggregate', 'rd'],
['aggregate', 'interfaces'],
['aggregate', 'target'],
['aggregate', 'state'],
['aggregate', 'active']]
module = AnsibleModule(argument_spec=argument_spec, module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True) supports_check_mode=True,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive)
warnings = list() warnings = list()
check_args(module, warnings) check_args(module, warnings)
@ -209,13 +237,20 @@ def main():
('target', 'vrf-target/community'), ('target', 'vrf-target/community'),
]) ])
module.params['type'] = 'vrf' params = to_param_list(module)
requests = list()
for param in params:
item = copy(param)
item['type'] = 'vrf'
want = map_params_to_obj(module, param_to_xpath_map) want = map_params_to_obj(module, param_to_xpath_map, param=item)
ele = map_obj_to_ele(module, want, top) requests.append(map_obj_to_ele(module, want, top, param=item))
with locked_config(module): with locked_config(module):
diff = load_config(module, tostring(ele), warnings, action='replace') for req in requests:
diff = load_config(module, tostring(req), warnings, action='replace')
commit = not module.check_mode commit = not module.check_mode
if diff: if diff:

@ -147,13 +147,141 @@
provider: "{{ netconf }}" provider: "{{ netconf }}"
register: config register: config
- name: Get running configuration
junos_rpc:
rpc: get-configuration
provider: "{{ netconf }}"
register: config
- assert: - assert:
that: that:
- "result.changed == true" - "result.changed == true"
- "'<name>ge-0/0/1</name>' not in config.xml" - "'<name>ge-0/0/1</name>' not in config.xml"
- name: Aggregate setup- delete interface ge-0/0/1
junos_interface:
name: ge-0/0/1
state: absent
provider: "{{ netconf }}"
register: result
- name: Aggregate setup- delete interface ge-0/0/2
junos_interface:
name: ge-0/0/2
state: absent
provider: "{{ netconf }}"
register: result
- name: Set interface on aggregate
junos_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\+ *ge-0/0/1")
- result.diff.prepared | search("\+ *description test-interface-1")
- result.diff.prepared | search("\+ *speed 1g")
- result.diff.prepared | search("\+ *mtu 512")
- result.diff.prepared | search("\+ *link-mode half-duplex")
- result.diff.prepared | search("\+ *description test-interface-2")
- result.diff.prepared | search("\+ *speed 10m")
- result.diff.prepared | search("\+ * mtu 256")
- result.diff.prepared | search("\+ *link-mode full-duplex")
- name: Set interface on aggregate (idempotent)
junos_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == false'
- name: Disable interface on aggregate
junos_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512, state: down}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256, state: down}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\+ *disable")
- name: Enable interface on aggregate
junos_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512, state: up}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256, state: up}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\- *disable")
- name: Deactivate interface configuration on aggregate
junos_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512, active: False}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256, active: False}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("! *inactive[:] ge-0/0/1")
- result.diff.prepared | search("! *inactive[:] ge-0/0/2")
- name: Activate interface configuration on aggregate
junos_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512, active: True}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256, active: True}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("! *active[:] ge-0/0/1")
- result.diff.prepared | search("! *active[:] ge-0/0/2")
- name: Delete interface on aggregate
junos_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512, state: absent}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256, state: absent}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\- *ge-0/0/1")
- result.diff.prepared | search("\- *description test-interface-1")
- result.diff.prepared | search("\- *speed 1g")
- result.diff.prepared | search("\- *mtu 512")
- result.diff.prepared | search("\- *link-mode half-duplex")
- result.diff.prepared | search("\- *description test-interface-2")
- result.diff.prepared | search("\- *speed 10m")
- result.diff.prepared | search("\- * mtu 256")
- result.diff.prepared | search("\- *link-mode full-duplex")
- name: Delete interface aggregate (idempotent)
junos_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512, state: absent}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256, state: absent}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == false'

@ -110,3 +110,151 @@
- assert: - assert:
that: that:
- "result.changed == false" - "result.changed == false"
- name: Aggregate setup- delete interface ge-0/0/1
junos_l3_interface:
name: ge-0/0/1
ipv4: 1.1.1.1
ipv6: fd5d:12c9:2201:1::1
state: absent
provider: "{{ netconf }}"
register: result
- name: Aggregate setup- delete interface ge-0/0/2
junos_l3_interface:
name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
state: absent
provider: "{{ netconf }}"
register: result
- name: Configure l3 interface in aggregate
junos_l3_interface:
aggregate:
- name: ge-0/0/1
ipv4: 1.1.1.1
ipv6: fd5d:12c9:2201:1::1
- name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- "'edit interfaces ge-0/0/1 unit 0 family inet' in result.diff.prepared"
- result.diff.prepared | search("\+ *address 1.1.1.1/32")
- "'edit interfaces ge-0/0/1 unit 0 family inet6' in result.diff.prepared"
- result.diff.prepared | search("\+ *address fd5d:12c9:2201:1::1/128")
- "'edit interfaces ge-0/0/2 unit 0 family inet' in result.diff.prepared"
- result.diff.prepared | search("\+ *address 2.2.2.2/32")
- "'edit interfaces ge-0/0/2 unit 0 family inet6' in result.diff.prepared"
- result.diff.prepared | search("\+ *address fd5d:12c9:2201:2::2/128")
- name: Configure l3 interface in aggregate (idempotent)
junos_l3_interface:
aggregate:
- name: ge-0/0/1
ipv4: 1.1.1.1
ipv6: fd5d:12c9:2201:1::1
active: True
- name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
active: True
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == false'
- name: Deactivate l3 interface configuration
junos_l3_interface:
aggregate:
- name: ge-0/0/1
ipv4: 1.1.1.1
ipv6: fd5d:12c9:2201:1::1
active: False
- name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
active: False
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("! *inactive[:] address 1.1.1.1/32")
- result.diff.prepared | search("! *inactive[:] address fd5d:12c9:2201:1::1/128")
- result.diff.prepared | search("! *inactive[:] address 2.2.2.2/32")
- result.diff.prepared | search("! *inactive[:] address fd5d:12c9:2201:2::2/128")
- name: Activate l3 interface configuration
junos_l3_interface:
aggregate:
- name: ge-0/0/1
ipv4: 1.1.1.1
ipv6: fd5d:12c9:2201:1::1
active: True
- name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
active: True
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("! *active[:] address 1.1.1.1/32")
- result.diff.prepared | search("! *active[:] address fd5d:12c9:2201:1::1/128")
- result.diff.prepared | search("! *active[:] address 2.2.2.2/32")
- result.diff.prepared | search("! *active[:] address fd5d:12c9:2201:2::2/128")
- name: Delete l3 interface configuration
junos_l3_interface:
aggregate:
- name: ge-0/0/1
ipv4: 1.1.1.1
ipv6: fd5d:12c9:2201:1::1
state: absent
- name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
state: absent
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- "'edit interfaces ge-0/0/1 unit 0 family inet' in result.diff.prepared"
- result.diff.prepared | search("\- *address 1.1.1.1/32")
- "'edit interfaces ge-0/0/1 unit 0 family inet6' in result.diff.prepared"
- result.diff.prepared | search("\- *address fd5d:12c9:2201:1::1/128")
- "'edit interfaces ge-0/0/2 unit 0 family inet' in result.diff.prepared"
- result.diff.prepared | search("\- *address 2.2.2.2/32")
- "'edit interfaces ge-0/0/2 unit 0 family inet6' in result.diff.prepared"
- result.diff.prepared | search("\- *address fd5d:12c9:2201:2::2/128")
- name: Delete l3 interface configuration (idempotent)
junos_l3_interface:
aggregate:
- name: ge-0/0/1
ipv4: 1.1.1.1
ipv6: fd5d:12c9:2201:1::1
state: absent
- name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
state: absent
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == false'

@ -296,3 +296,87 @@
- "'<size>64k</size>' not in config.xml" - "'<size>64k</size>' not in config.xml"
- "'<files>40</files>' not in config.xml" - "'<files>40</files>' not in config.xml"
- "'<log-rotate-frequency>20</log-rotate-frequency>' not in config.xml" - "'<log-rotate-frequency>20</log-rotate-frequency>' not in config.xml"
- name: Seup file logging using aggregate
junos_logging:
aggregate:
- {dest: file, name: test-1, facility: pfe, level: critical, state: absent}
- {dest: file, name: test-2, facility: kernel, level: emergency, state: absent}
provider: "{{ netconf }}"
register: result
- name: Configure file logging using aggregate
junos_logging:
aggregate:
- {dest: file, name: test-1, facility: pfe, level: critical, active: True}
- {dest: file, name: test-2, facility: kernel, level: emergency, active: True}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\+ *file test-1")
- result.diff.prepared | search("\+ *pfe critical")
- result.diff.prepared | search("\+ *file test-2")
- result.diff.prepared | search("\+ *kernel emergency")
- name: Deactivate file logging configuration using aggregate
junos_logging:
aggregate:
- {dest: file, name: test-1, facility: pfe, level: critical, active: False}
- {dest: file, name: test-2, facility: kernel, level: emergency, active: False}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("! *inactive[:] file test-1")
- result.diff.prepared | search("! *inactive[:] pfe")
- result.diff.prepared | search("! *inactive[:] file test-2")
- result.diff.prepared | search("! *inactive[:] kernel")
- name: activate file logging configuration using aggregate
junos_logging:
aggregate:
- {dest: file, name: test-1, facility: pfe, level: critical, active: True}
- {dest: file, name: test-2, facility: kernel, level: emergency, active: True}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("! *active[:] file test-1")
- result.diff.prepared | search("! *active[:] pfe")
- result.diff.prepared | search("! *active[:] file test-2")
- result.diff.prepared | search("! *active[:] kernel")
- name: Delete file logging using aggregate
junos_logging:
aggregate:
- {dest: file, name: test-1, facility: pfe, level: critical, state: absent}
- {dest: file, name: test-2, facility: kernel, level: emergency, state: absent}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\- *file test-1")
- result.diff.prepared | search("\- *pfe critical")
- result.diff.prepared | search("\- *file test-2")
- result.diff.prepared | search("\- *kernel emergency")
- name: Delete file logging using aggregate (idempotent)
junos_logging:
aggregate:
- {dest: file, name: test-1, facility: pfe, level: critical, state: absent}
- {dest: file, name: test-2, facility: kernel, level: emergency, state: absent}
provider: "{{ netconf }}"
register: result
- assert:
that:
- "result.changed == false"

@ -140,3 +140,102 @@
- assert: - assert:
that: that:
- "result.changed == false" - "result.changed == false"
- name: Setup static route for aggegrate
junos_static_route:
aggregate:
- {address: 4.4.4.0/24, state: absent}
- {address: 5.5.5.0/24, state: absent}
provider: "{{ netconf }}"
- name: Confgiure static route using aggegrate
junos_static_route:
aggregate:
- {address: 4.4.4.0/24, next_hop: 3.3.3.3, preference: 10, qualified_next_hop: 5.5.5.5, qualified_preference: 30}
- {address: 5.5.5.0/24, next_hop: 6.6.6.6, preference: 11, qualified_next_hop: 7.7.7.7, qualified_preference: 12}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\+ *route 4.4.4.0/24")
- result.diff.prepared | search("\+ *next-hop 3.3.3.3")
- result.diff.prepared | search("\+ *qualified-next-hop 5.5.5.5")
- result.diff.prepared | search("\+ *preference 30")
- result.diff.prepared | search("\+ *preference 10")
- result.diff.prepared | search("\+ *route 5.5.5.0/24")
- result.diff.prepared | search("\+ *next-hop 6.6.6.6")
- result.diff.prepared | search("\+ *qualified-next-hop 7.7.7.7")
- result.diff.prepared | search("\+ *preference 12")
- result.diff.prepared | search("\+ *preference 11")
- name: Deactivate static route configuration using aggegrate
junos_static_route:
aggregate:
- {address: 4.4.4.0/24, next_hop: 3.3.3.3, preference: 10, qualified_next_hop: 5.5.5.5, qualified_preference: 30, active: False}
- {address: 5.5.5.0/24, next_hop: 6.6.6.6, preference: 11, qualified_next_hop: 7.7.7.7, qualified_preference: 12, active: False}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("! *inactive[:] route 4.4.4.0/24")
- result.diff.prepared | search("! *inactive[:] qualified-next-hop 5.5.5.5")
- result.diff.prepared | search("! *inactive[:] preference")
- result.diff.prepared | search("! *inactive[:] route 5.5.5.0/24")
- result.diff.prepared | search("! *inactive[:] qualified-next-hop 7.7.7.7")
- result.diff.prepared | search("! *inactive[:] preference")
- name: Activate static route configuration using aggegrate
junos_static_route:
aggregate:
- {address: 4.4.4.0/24, next_hop: 3.3.3.3, preference: 10, qualified_next_hop: 5.5.5.5, qualified_preference: 30, active: True}
- {address: 5.5.5.0/24, next_hop: 6.6.6.6, preference: 11, qualified_next_hop: 7.7.7.7, qualified_preference: 12, active: True}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("! *active[:] route 4.4.4.0/24")
- result.diff.prepared | search("! *active[:] qualified-next-hop 5.5.5.5")
- result.diff.prepared | search("! *active[:] preference")
- result.diff.prepared | search("! *active[:] route 5.5.5.0/24")
- result.diff.prepared | search("! *active[:] qualified-next-hop 7.7.7.7")
- result.diff.prepared | search("! *active[:] preference")
- name: Delete static route configuration using aggegrate
junos_static_route:
aggregate:
- {address: 4.4.4.0/24, state: absent}
- {address: 5.5.5.0/24, state: absent}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\- *route 4.4.4.0/24")
- result.diff.prepared | search("\- *next-hop 3.3.3.3")
- result.diff.prepared | search("\- *qualified-next-hop 5.5.5.5")
- result.diff.prepared | search("\- *preference 30")
- result.diff.prepared | search("\- *preference 10")
- result.diff.prepared | search("\- *route 5.5.5.0/24")
- result.diff.prepared | search("\- *next-hop 6.6.6.6")
- result.diff.prepared | search("\- *qualified-next-hop 7.7.7.7")
- result.diff.prepared | search("\- *preference 12")
- result.diff.prepared | search("\- *preference 11")
- name: Delete static route configuration using aggegrate (idempotent)
junos_static_route:
aggregate:
- {address: 4.4.4.0/24, state: absent}
- {address: 5.5.5.0/24, state: absent}
provider: "{{ netconf }}"
register: result
- assert:
that:
- "result.changed == false"

@ -102,3 +102,83 @@
that: that:
- "result.changed == true" - "result.changed == true"
- "'<name>test-vlan</name>' not in config.xml" - "'<name>test-vlan</name>' not in config.xml"
- name: Setup vlan configuration for aggregate
junos_vlan:
aggregate:
- { vlan_id: 159, name: test_vlan_1, state: absent }
- { vlan_id: 160, name: test_vlan_2, state: absent }
provider: "{{ netconf }}"
- name: Create vlan configuration using aggregate
junos_vlan:
aggregate:
- { vlan_id: 159, name: test_vlan_1, description: test vlan-1, state: present }
- { vlan_id: 160, name: test_vlan_2, description: test vlan-2, state: present }
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\+ *vlans")
- result.diff.prepared | search("\+ *test_vlan_1")
- result.diff.prepared | search("\+ *vlan-id 159")
- result.diff.prepared | search("\+ *test_vlan_2")
- result.diff.prepared | search("\+ *vlan-id 160")
- name: Deactivate vlan configuration using aggregate
junos_vlan:
aggregate:
- { vlan_id: 159, name: test_vlan_1, description: test vlan-1, active: False }
- { vlan_id: 160, name: test_vlan_2, description: test vlan-2, active: False }
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("! *inactive[:] test_vlan_1")
- result.diff.prepared | search("! *inactive[:] test_vlan_2")
- name: activate vlan configuration using aggregate
junos_vlan:
aggregate:
- { vlan_id: 159, name: test_vlan_1, description: test vlan-1, active: True }
- { vlan_id: 160, name: test_vlan_2, description: test vlan-2, active: True }
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("! *active[:] test_vlan_1")
- result.diff.prepared | search("! *active[:] test_vlan_2")
- name: Delete vlan configuration using aggregate
junos_vlan:
aggregate:
- { vlan_id: 159, name: test_vlan_1, state: absent }
- { vlan_id: 160, name: test_vlan_2, state: absent }
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\- *test_vlan_1")
- result.diff.prepared | search("\- *vlan-id 159")
- result.diff.prepared | search("\- *test_vlan_2")
- result.diff.prepared | search("\- *vlan-id 160")
- name: Delete vlan configuration using aggregate (idempotent)
junos_vlan:
aggregate:
- { vlan_id: 159, name: test_vlan_1, state: absent }
- { vlan_id: 160, name: test_vlan_2, state: absent }
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == false'

@ -167,3 +167,171 @@
- assert: - assert:
that: that:
- "result.changed == false" - "result.changed == false"
- name: Setup vrf using aggregate
junos_vrf:
aggregate:
- name: test-1
state: absent
- name: test-2
state: absent
provider: "{{ netconf }}"
register: result
- name: Create vrf using aggregate
junos_vrf:
aggregate:
- name: test-1
description: test-vrf-1
interfaces:
- ge-0/0/3
- ge-0/0/2
rd: 1.1.1.1:10
target: target:65514:113
state: present
- name: test-2
description: test-vrf-2
interfaces:
- ge-0/0/4
- ge-0/0/5
rd: 2.2.2.2:10
target: target:65515:114
state: present
provider: "{{ netconf }}"
register: result
- assert:
that:
- "result.changed == true"
- result.diff.prepared | search("\+ *test-1")
- result.diff.prepared | search("\+ *description test-vrf-1")
- result.diff.prepared | search("\+ *instance-type vrf")
- result.diff.prepared | search("\+ *interface ge-0/0/2.0")
- result.diff.prepared | search("\+ *interface ge-0/0/3.0")
- result.diff.prepared | search("\+ *route-distinguisher 1.1.1.1:10")
- result.diff.prepared | search("\+ *vrf-target target:65514:113")
- result.diff.prepared | search("\+ *test-2")
- result.diff.prepared | search("\+ *description test-vrf-2")
- result.diff.prepared | search("\+ *instance-type vrf")
- result.diff.prepared | search("\+ *interface ge-0/0/4.0")
- result.diff.prepared | search("\+ *interface ge-0/0/5.0")
- result.diff.prepared | search("\+ *route-distinguisher 2.2.2.2:10")
- result.diff.prepared | search("\+ *vrf-target target:65515:114")
- name: Deactivate vrf configuration using aggregate
junos_vrf:
aggregate:
- name: test-1
description: test-vrf-1
interfaces:
- ge-0/0/3
- ge-0/0/2
rd: 1.1.1.1:10
target: target:65514:113
state: present
active: False
- name: test-2
description: test-vrf-2
interfaces:
- ge-0/0/4
- ge-0/0/5
rd: 2.2.2.2:10
target: target:65515:114
state: present
active: False
provider: "{{ netconf }}"
register: result
- assert:
that:
- "result.changed == true"
- "'edit routing-instances test-1' in result.diff.prepared"
- result.diff.prepared | search("! *inactive[:] interface ge-0/0/2.0")
- result.diff.prepared | search("! *inactive[:] interface ge-0/0/3.0")
- result.diff.prepared | search("! *inactive[:] route-distinguisher")
- result.diff.prepared | search("! *inactive[:] vrf-target")
- "'edit routing-instances test-2' in result.diff.prepared"
- result.diff.prepared | search("! *inactive[:] interface ge-0/0/4.0")
- result.diff.prepared | search("! *inactive[:] interface ge-0/0/5.0")
- result.diff.prepared | search("! *inactive[:] route-distinguisher")
- result.diff.prepared | search("! *inactive[:] vrf-target")
- name: Deactivate vrf configuration using aggregate
junos_vrf:
aggregate:
- name: test-1
description: test-vrf-1
interfaces:
- ge-0/0/3
- ge-0/0/2
rd: 1.1.1.1:10
target: target:65514:113
state: present
active: True
- name: test-2
description: test-vrf-2
interfaces:
- ge-0/0/4
- ge-0/0/5
rd: 2.2.2.2:10
target: target:65515:114
state: present
active: True
provider: "{{ netconf }}"
register: result
- assert:
that:
- "result.changed == true"
- "'edit routing-instances test-1' in result.diff.prepared"
- result.diff.prepared | search("! *active[:] interface ge-0/0/2.0")
- result.diff.prepared | search("! *active[:] interface ge-0/0/3.0")
- result.diff.prepared | search("! *active[:] route-distinguisher")
- result.diff.prepared | search("! *active[:] vrf-target")
- "'edit routing-instances test-2' in result.diff.prepared"
- result.diff.prepared | search("! *active[:] interface ge-0/0/4.0")
- result.diff.prepared | search("! *active[:] interface ge-0/0/5.0")
- result.diff.prepared | search("! *active[:] route-distinguisher")
- result.diff.prepared | search("! *active[:] vrf-target")
- name: Delete vrf configuration using aggregate
junos_vrf:
aggregate:
- name: test-1
state: absent
- name: test-2
state: absent
provider: "{{ netconf }}"
register: result
- assert:
that:
- "result.changed == true"
- result.diff.prepared | search("\- *test-1")
- result.diff.prepared | search("\- *description test-vrf-1")
- result.diff.prepared | search("\- *instance-type vrf")
- result.diff.prepared | search("\- *interface ge-0/0/2.0")
- result.diff.prepared | search("\- *interface ge-0/0/3.0")
- result.diff.prepared | search("\- *route-distinguisher 1.1.1.1:10")
- result.diff.prepared | search("\- *vrf-target target:65514:113")
- result.diff.prepared | search("\- *test-2")
- result.diff.prepared | search("\- *description test-vrf-2")
- result.diff.prepared | search("\- *instance-type vrf")
- result.diff.prepared | search("\- *interface ge-0/0/4.0")
- result.diff.prepared | search("\- *interface ge-0/0/5.0")
- result.diff.prepared | search("\- *route-distinguisher 2.2.2.2:10")
- result.diff.prepared | search("\- *vrf-target target:65515:114")
- name: Delete vrf configuration using aggregate (idempotent)
junos_vrf:
aggregate:
- name: test-1
state: absent
- name: test-2
state: absent
provider: "{{ netconf }}"
register: result
- assert:
that:
- "result.changed == false"

@ -83,7 +83,7 @@
- assert: - assert:
that: that:
- "result.changed == true" - "result.changed == true"
- "'<disable/>' in config.xml" - result.diff.prepared | search("\+ *disable")
- "'<name>ge-0/0/1</name>' in config.xml" - "'<name>ge-0/0/1</name>' in config.xml"
- name: Enable interface - name: Enable interface
@ -103,7 +103,7 @@
- assert: - assert:
that: that:
- "result.changed == true" - "result.changed == true"
- "'<disable/>' not in config.xml" - result.diff.prepared | search("\- *disable")
- "'<name>ge-0/0/1</name>' in config.xml" - "'<name>ge-0/0/1</name>' in config.xml"
- name: Delete interface - name: Delete interface
@ -124,3 +124,109 @@
that: that:
- "result.changed == true" - "result.changed == true"
- "'<name>ge-0/0/1</name>' not in config.xml" - "'<name>ge-0/0/1</name>' not in config.xml"
- name: Aggregate setup- delete interface ge-0/0/1
net_interface:
name: ge-0/0/1
state: absent
provider: "{{ netconf }}"
register: result
- name: Aggregate setup- delete interface ge-0/0/2
net_interface:
name: ge-0/0/2
state: absent
provider: "{{ netconf }}"
register: result
- name: Set interface on aggregate
net_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\+ *ge-0/0/1")
- result.diff.prepared | search("\+ *description test-interface-1")
- result.diff.prepared | search("\+ *speed 1g")
- result.diff.prepared | search("\+ *mtu 512")
- result.diff.prepared | search("\+ *link-mode half-duplex")
- result.diff.prepared | search("\+ *description test-interface-2")
- result.diff.prepared | search("\+ *speed 10m")
- result.diff.prepared | search("\+ * mtu 256")
- result.diff.prepared | search("\+ *link-mode full-duplex")
- name: Set interface on aggregate (idempotent)
net_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == false'
- name: Disable interface on aggregate
net_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512, state: down}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256, state: down}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\+ *disable")
- name: Enable interface on aggregate
net_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512, state: up}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256, state: up}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\- *disable")
- name: Delete interface on aggregate
net_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512, state: absent}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256, state: absent}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\- *ge-0/0/1")
- result.diff.prepared | search("\- *description test-interface-1")
- result.diff.prepared | search("\- *speed 1g")
- result.diff.prepared | search("\- *mtu 512")
- result.diff.prepared | search("\- *link-mode half-duplex")
- result.diff.prepared | search("\- *description test-interface-2")
- result.diff.prepared | search("\- *speed 10m")
- result.diff.prepared | search("\- * mtu 256")
- result.diff.prepared | search("\- *link-mode full-duplex")
- name: Delete interface aggregate (idempotent)
net_interface:
aggregate:
- { name: ge-0/0/1, description: test-interface-1, speed: 1g, duplex: half, mtu: 512, state: absent}
- { name: ge-0/0/2, description: test-interface-2, speed: 10m, duplex: full, mtu: 256, state: absent}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == false'

@ -80,3 +80,107 @@
- assert: - assert:
that: that:
- "result.changed == false" - "result.changed == false"
- name: Aggregate setup- delete interface ge-0/0/1
net_l3_interface:
name: ge-0/0/1
ipv4: 1.1.1.1
ipv6: fd5d:12c9:2201:1::1
state: absent
provider: "{{ netconf }}"
register: result
- name: Aggregate setup- delete interface ge-0/0/2
net_l3_interface:
name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
state: absent
provider: "{{ netconf }}"
register: result
- name: Configure l3 interface in aggregate
net_l3_interface:
aggregate:
- name: ge-0/0/1
ipv4: 1.1.1.1
ipv6: fd5d:12c9:2201:1::1
- name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- "'edit interfaces ge-0/0/1 unit 0 family inet' in result.diff.prepared"
- result.diff.prepared | search("\+ *address 1.1.1.1/32")
- "'edit interfaces ge-0/0/1 unit 0 family inet6' in result.diff.prepared"
- result.diff.prepared | search("\+ *address fd5d:12c9:2201:1::1/128")
- "'edit interfaces ge-0/0/2 unit 0 family inet' in result.diff.prepared"
- result.diff.prepared | search("\+ *address 2.2.2.2/32")
- "'edit interfaces ge-0/0/2 unit 0 family inet6' in result.diff.prepared"
- result.diff.prepared | search("\+ *address fd5d:12c9:2201:2::2/128")
- name: Configure l3 interface in aggregate (idempotent)
net_l3_interface:
aggregate:
- name: ge-0/0/1
ipv4: 1.1.1.1
ipv6: fd5d:12c9:2201:1::1
active: True
- name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
active: True
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == false'
- name: Delete l3 interface configuration
net_l3_interface:
aggregate:
- name: ge-0/0/1
ipv4: 1.1.1.1
ipv6: fd5d:12c9:2201:1::1
state: absent
- name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
state: absent
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- "'edit interfaces ge-0/0/1 unit 0 family inet' in result.diff.prepared"
- result.diff.prepared | search("\- *address 1.1.1.1/32")
- "'edit interfaces ge-0/0/1 unit 0 family inet6' in result.diff.prepared"
- result.diff.prepared | search("\- *address fd5d:12c9:2201:1::1/128")
- "'edit interfaces ge-0/0/2 unit 0 family inet' in result.diff.prepared"
- result.diff.prepared | search("\- *address 2.2.2.2/32")
- "'edit interfaces ge-0/0/2 unit 0 family inet6' in result.diff.prepared"
- result.diff.prepared | search("\- *address fd5d:12c9:2201:2::2/128")
- name: Delete l3 interface configuration (idempotent)
net_l3_interface:
aggregate:
- name: ge-0/0/1
ipv4: 1.1.1.1
ipv6: fd5d:12c9:2201:1::1
state: absent
- name: ge-0/0/2
ipv4: 2.2.2.2
ipv6: fd5d:12c9:2201:2::2
state: absent
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == false'

@ -122,3 +122,35 @@
that: that:
- "result.changed == true" - "result.changed == true"
- "'<console>' not in config.xml" - "'<console>' not in config.xml"
- name: Configure file logging using aggregate
net_logging:
aggregate:
- {dest: file, name: test-1, facility: pfe, level: critical, active: True}
- {dest: file, name: test-2, facility: kernel, level: emergency, active: True}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\+ *file test-1")
- result.diff.prepared | search("\+ *pfe critical")
- result.diff.prepared | search("\+ *file test-2")
- result.diff.prepared | search("\+ *kernel emergency")
- name: Delete file logging using aggregate
net_logging:
aggregate:
- {dest: file, name: test-1, facility: pfe, level: critical, state: absent}
- {dest: file, name: test-2, facility: kernel, level: emergency, state: absent}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\- *file test-1")
- result.diff.prepared | search("\- *pfe critical")
- result.diff.prepared | search("\- *file test-2")
- result.diff.prepared | search("\- *kernel emergency")

@ -69,3 +69,66 @@
- assert: - assert:
that: that:
- "result.changed == false" - "result.changed == false"
- name: Setup static route for aggegrate
net_static_route:
aggregate:
- {address: 4.4.4.0/24, state: absent}
- {address: 5.5.5.0/24, state: absent}
provider: "{{ netconf }}"
- name: Confgiure static route using aggegrate
net_static_route:
aggregate:
- {address: 4.4.4.0/24, next_hop: 3.3.3.3, preference: 10, qualified_next_hop: 5.5.5.5, qualified_preference: 30}
- {address: 5.5.5.0/24, next_hop: 6.6.6.6, preference: 11, qualified_next_hop: 7.7.7.7, qualified_preference: 12}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\+ *route 4.4.4.0/24")
- result.diff.prepared | search("\+ *next-hop 3.3.3.3")
- result.diff.prepared | search("\+ *qualified-next-hop 5.5.5.5")
- result.diff.prepared | search("\+ *preference 30")
- result.diff.prepared | search("\+ *preference 10")
- result.diff.prepared | search("\+ *route 5.5.5.0/24")
- result.diff.prepared | search("\+ *next-hop 6.6.6.6")
- result.diff.prepared | search("\+ *qualified-next-hop 7.7.7.7")
- result.diff.prepared | search("\+ *preference 12")
- result.diff.prepared | search("\+ *preference 11")
- name: Delete static route configuration using aggegrate
net_static_route:
aggregate:
- {address: 4.4.4.0/24, state: absent}
- {address: 5.5.5.0/24, state: absent}
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\- *route 4.4.4.0/24")
- result.diff.prepared | search("\- *next-hop 3.3.3.3")
- result.diff.prepared | search("\- *qualified-next-hop 5.5.5.5")
- result.diff.prepared | search("\- *preference 30")
- result.diff.prepared | search("\- *preference 10")
- result.diff.prepared | search("\- *route 5.5.5.0/24")
- result.diff.prepared | search("\- *next-hop 6.6.6.6")
- result.diff.prepared | search("\- *qualified-next-hop 7.7.7.7")
- result.diff.prepared | search("\- *preference 12")
- result.diff.prepared | search("\- *preference 11")
- name: Delete static route configuration using aggegrate (idempotent)
net_static_route:
aggregate:
- {address: 4.4.4.0/24, state: absent}
- {address: 5.5.5.0/24, state: absent}
provider: "{{ netconf }}"
register: result
- assert:
that:
- "result.changed == false"

@ -25,7 +25,7 @@
that: that:
- "result.changed == true" - "result.changed == true"
- "'<name>test_user</name>' in config.xml" - "'<name>test_user</name>' in config.xml"
- "'<class>read-only</class>' in config.xml" - "'<class>operator</class>' in config.xml"
- name: Create user again (idempotent) - name: Create user again (idempotent)
net_user: net_user:

@ -58,3 +58,55 @@
that: that:
- "result.changed == true" - "result.changed == true"
- "'<name>test-vlan</name>' not in config.xml" - "'<name>test-vlan</name>' not in config.xml"
- name: Setup vlan configuration for aggregate
net_vlan:
aggregate:
- { vlan_id: 159, name: test_vlan_1, state: absent }
- { vlan_id: 160, name: test_vlan_2, state: absent }
provider: "{{ netconf }}"
- name: Create vlan configuration using aggregate
net_vlan:
aggregate:
- { vlan_id: 159, name: test_vlan_1, description: test vlan-1, state: present }
- { vlan_id: 160, name: test_vlan_2, description: test vlan-2, state: present }
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\+ *vlans")
- result.diff.prepared | search("\+ *test_vlan_1")
- result.diff.prepared | search("\+ *vlan-id 159")
- result.diff.prepared | search("\+ *test_vlan_2")
- result.diff.prepared | search("\+ *vlan-id 160")
- name: Delete vlan configuration using aggregate
junos_vlan:
aggregate:
- { vlan_id: 159, name: test_vlan_1, state: absent }
- { vlan_id: 160, name: test_vlan_2, state: absent }
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == true'
- result.diff.prepared | search("\- *test_vlan_1")
- result.diff.prepared | search("\- *vlan-id 159")
- result.diff.prepared | search("\- *test_vlan_2")
- result.diff.prepared | search("\- *vlan-id 160")
- name: Delete vlan configuration using aggregate (idempotent)
junos_vlan:
aggregate:
- { vlan_id: 159, name: test_vlan_1, state: absent }
- { vlan_id: 160, name: test_vlan_2, state: absent }
provider: "{{ netconf }}"
register: result
- assert:
that:
- 'result.changed == false'

@ -23,13 +23,13 @@
- assert: - assert:
that: that:
- "result.changed == true" - "result.changed == true"
- "'+ test-1' in result.diff.prepared" - result.diff.prepared | search("\+ *test-1")
- "'+ description test-vrf-1;' in result.diff.prepared" - result.diff.prepared | search("\+ *description test-vrf-1")
- "'+ instance-type vrf;' in result.diff.prepared" - result.diff.prepared | search("\+ *instance-type vrf")
- "'+ interface ge-0/0/5.0;' in result.diff.prepared" - result.diff.prepared | search("\+ *interface ge-0/0/5.0")
- "'+ interface ge-0/0/6.0;' in result.diff.prepared" - result.diff.prepared | search("\+ *interface ge-0/0/6.0")
- "'+ route-distinguisher 3.3.3.3:10;' in result.diff.prepared" - result.diff.prepared | search("\+ *route-distinguisher 3.3.3.3:10")
- "'+ vrf-target target:65513:111;' in result.diff.prepared" - result.diff.prepared | search("\+ *vrf-target target:65513:111")
- name: Configure vrf and its parameter (idempotent) - name: Configure vrf and its parameter (idempotent)
net_vrf: net_vrf:
@ -65,18 +65,18 @@
that: that:
- "result.changed == true" - "result.changed == true"
- "'[edit routing-instances test-1]' in result.diff.prepared" - "'[edit routing-instances test-1]' in result.diff.prepared"
- "'+ interface ge-0/0/2.0;' in result.diff.prepared" - result.diff.prepared | search("\+ *interface ge-0/0/2.0")
- "'+ interface ge-0/0/3.0;' in result.diff.prepared" - result.diff.prepared | search("\+ *interface ge-0/0/3.0")
- "'- interface ge-0/0/5.0;' in result.diff.prepared" - result.diff.prepared | search("\- *interface ge-0/0/5.0")
- "'- interface ge-0/0/6.0;' in result.diff.prepared" - result.diff.prepared | search("\- *interface ge-0/0/6.0")
- "'[edit routing-instances test-1]' in result.diff.prepared" - "'[edit routing-instances test-1]' in result.diff.prepared"
- "'- route-distinguisher 3.3.3.3:10;' in result.diff.prepared" - result.diff.prepared | search("\- *route-distinguisher 3.3.3.3:10")
- "'+ route-distinguisher 1.1.1.1:10;' in result.diff.prepared" - result.diff.prepared | search("\+ *route-distinguisher 1.1.1.1:10")
- "'- vrf-target target:65513:111;' in result.diff.prepared" - result.diff.prepared | search("\- *vrf-target target:65513:111")
- "'+ vrf-target target:65514:113;' in result.diff.prepared" - result.diff.prepared | search("\+ *vrf-target target:65514:113")
- name: Deactivate vrf - name: Delete vrf
net_vrf: net_vrf:
name: test-1 name: test-1
description: test-vrf-1 description: test-vrf-1
@ -85,24 +85,22 @@
- ge-0/0/2 - ge-0/0/2
rd: 1.1.1.1:10 rd: 1.1.1.1:10
target: target:65514:113 target: target:65514:113
state: present state: absent
active: False
provider: "{{ netconf }}" provider: "{{ netconf }}"
register: result register: result
- assert: - assert:
that: that:
- "result.changed == true" - "result.changed == true"
- "'[edit routing-instances]' in result.diff.prepared" - result.diff.prepared | search("\- *test-1")
- "'! inactive: test-1' in result.diff.prepared" - result.diff.prepared | search("\- *description test-vrf-1")
- "'[edit routing-instances test-1]' in result.diff.prepared" - result.diff.prepared | search("\- *instance-type vrf")
- "'! inactive: interface ge-0/0/2.0' in result.diff.prepared" - result.diff.prepared | search("\- *interface ge-0/0/2.0")
- "'! inactive: interface ge-0/0/3.0' in result.diff.prepared" - result.diff.prepared | search("\- *interface ge-0/0/3.0")
- "'[edit routing-instances test-1]' in result.diff.prepared" - result.diff.prepared | search("\- *route-distinguisher 1.1.1.1:10")
- "'! inactive: route-distinguisher' in result.diff.prepared" - result.diff.prepared | search("\- *vrf-target target:65514:113")
- "'! inactive: vrf-target' in result.diff.prepared"
- name: Activate vrf - name: Delete vrf (idempotent)
net_vrf: net_vrf:
name: test-1 name: test-1
description: test-vrf-1 description: test-vrf-1
@ -111,57 +109,98 @@
- ge-0/0/2 - ge-0/0/2
rd: 1.1.1.1:10 rd: 1.1.1.1:10
target: target:65514:113 target: target:65514:113
state: present state: absent
active: True
provider: "{{ netconf }}" provider: "{{ netconf }}"
register: result register: result
- assert: - assert:
that: that:
- "result.changed == true" - "result.changed == false"
- "'[edit routing-instances]' in result.diff.prepared"
- "'! active: test-1' in result.diff.prepared"
- "'[edit routing-instances test-1]' in result.diff.prepared"
- "'! active: interface ge-0/0/2.0' in result.diff.prepared"
- "'! active: interface ge-0/0/3.0' in result.diff.prepared"
- "'[edit routing-instances test-1]' in result.diff.prepared"
- "'! active: route-distinguisher' in result.diff.prepared"
- "'! active: vrf-target' in result.diff.prepared"
- name: Delete vrf - name: Setup vrf using aggregate
net_vrf: net_vrf:
name: test-1 aggregate:
- name: test-1
state: absent
- name: test-2
state: absent
provider: "{{ netconf }}"
register: result
- name: Create vrf using aggregate
net_vrf:
aggregate:
- name: test-1
description: test-vrf-1 description: test-vrf-1
interfaces: interfaces:
- ge-0/0/3 - ge-0/0/3
- ge-0/0/2 - ge-0/0/2
rd: 1.1.1.1:10 rd: 1.1.1.1:10
target: target:65514:113 target: target:65514:113
state: absent state: present
- name: test-2
description: test-vrf-2
interfaces:
- ge-0/0/4
- ge-0/0/5
rd: 2.2.2.2:10
target: target:65515:114
state: present
provider: "{{ netconf }}" provider: "{{ netconf }}"
register: result register: result
- assert: - assert:
that: that:
- "result.changed == true" - "result.changed == true"
- "'[edit routing-instances]' in result.diff.prepared" - result.diff.prepared | search("\+ *test-1")
- "'- test-1' in result.diff.prepared" - result.diff.prepared | search("\+ *description test-vrf-1")
- "'- description test-vrf-1;' in result.diff.prepared" - result.diff.prepared | search("\+ *instance-type vrf")
- "'- instance-type vrf;' in result.diff.prepared" - result.diff.prepared | search("\+ *interface ge-0/0/2.0")
- "'- interface ge-0/0/2.0;' in result.diff.prepared" - result.diff.prepared | search("\+ *interface ge-0/0/3.0")
- "'- interface ge-0/0/3.0;' in result.diff.prepared" - result.diff.prepared | search("\+ *route-distinguisher 1.1.1.1:10")
- "'- route-distinguisher 1.1.1.1:10;' in result.diff.prepared" - result.diff.prepared | search("\+ *vrf-target target:65514:113")
- "'- vrf-target target:65514:113;' in result.diff.prepared" - result.diff.prepared | search("\+ *test-2")
- result.diff.prepared | search("\+ *description test-vrf-2")
- result.diff.prepared | search("\+ *instance-type vrf")
- result.diff.prepared | search("\+ *interface ge-0/0/4.0")
- result.diff.prepared | search("\+ *interface ge-0/0/5.0")
- result.diff.prepared | search("\+ *route-distinguisher 2.2.2.2:10")
- result.diff.prepared | search("\+ *vrf-target target:65515:114")
- name: Delete vrf configuration using aggregate
net_vrf:
aggregate:
- name: test-1
state: absent
- name: test-2
state: absent
provider: "{{ netconf }}"
register: result
- name: Delete vrf (idempotent) - assert:
that:
- "result.changed == true"
- result.diff.prepared | search("\- *test-1")
- result.diff.prepared | search("\- *description test-vrf-1")
- result.diff.prepared | search("\- *instance-type vrf")
- result.diff.prepared | search("\- *interface ge-0/0/2.0")
- result.diff.prepared | search("\- *interface ge-0/0/3.0")
- result.diff.prepared | search("\- *route-distinguisher 1.1.1.1:10")
- result.diff.prepared | search("\- *vrf-target target:65514:113")
- result.diff.prepared | search("\- *test-2")
- result.diff.prepared | search("\- *description test-vrf-2")
- result.diff.prepared | search("\- *instance-type vrf")
- result.diff.prepared | search("\- *interface ge-0/0/4.0")
- result.diff.prepared | search("\- *interface ge-0/0/5.0")
- result.diff.prepared | search("\- *route-distinguisher 2.2.2.2:10")
- result.diff.prepared | search("\- *vrf-target target:65515:114")
- name: Delete vrf configuration using aggregate (idempotent)
net_vrf: net_vrf:
name: test-1 aggregate:
description: test-vrf-1 - name: test-1
interfaces: state: absent
- ge-0/0/3 - name: test-2
- ge-0/0/2
rd: 1.1.1.1:10
target: target:65514:113
state: absent state: absent
provider: "{{ netconf }}" provider: "{{ netconf }}"
register: result register: result

Loading…
Cancel
Save