mirror of https://github.com/ansible/ansible.git
Migrated to junipernetworks.junos
parent
0c8d6f0805
commit
35063de590
@ -1,22 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The arg spec for the junos facts module.
|
||||
"""
|
||||
|
||||
|
||||
class FactsArgs(object):
|
||||
""" The arg spec for the junos facts module
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
argument_spec = {
|
||||
'gather_subset': dict(default=['!config'], type='list'),
|
||||
'config_format': dict(default='text', choices=['xml', 'text', 'set', 'json']),
|
||||
'gather_network_resources': dict(type='list'),
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The arg spec for the junos_interfaces module
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class InterfacesArgs(object):
|
||||
"""The arg spec for the junos_interfaces module
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
argument_spec = {'config': {'elements': 'dict',
|
||||
'options': {'description': {'type': 'str'},
|
||||
'duplex': {'choices': ['automatic',
|
||||
'full-duplex',
|
||||
'half-duplex'],
|
||||
'type': 'str'},
|
||||
'enabled': {'default': True, 'type': 'bool'},
|
||||
'hold_time': {'options': {'down': {'type': 'int'},
|
||||
'up': {'type': 'int'}},
|
||||
'required_together': [['down', 'up']],
|
||||
'type': 'dict'},
|
||||
'mtu': {'type': 'int'},
|
||||
'name': {'required': True, 'type': 'str'},
|
||||
'speed': {'type': 'str'}},
|
||||
'type': 'list'},
|
||||
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
|
||||
'default': 'merged',
|
||||
'type': 'str'}}
|
@ -1,49 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The arg spec for the junos_l2_interfaces module
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class L2_interfacesArgs(object):
|
||||
"""The arg spec for the junos_l2_interfaces module
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
argument_spec = {'config': {'elements': 'dict',
|
||||
'options': {'access': {'type': 'dict', 'options': {'vlan': {'type': 'str'}}},
|
||||
'name': {'required': True, 'type': 'str'},
|
||||
'trunk': {'type': 'dict', 'options': {'allowed_vlans': {'type': 'list'},
|
||||
'native_vlan': {'type': 'str'}}},
|
||||
'unit': {'type': 'int'},
|
||||
'enhanced_layer': {'type': 'bool'}},
|
||||
'type': 'list'},
|
||||
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
|
||||
'default': 'merged',
|
||||
'type': 'str'}}
|
@ -1,37 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
"""
|
||||
The arg spec for the junos_l3_interfaces module
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class L3_interfacesArgs(object): # pylint: disable=R0903
|
||||
"""The arg spec for the junos_l3_interfaces module
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
argument_spec = {'config': {'elements': 'dict', 'options':
|
||||
{
|
||||
'ipv4': {'elements': 'dict',
|
||||
'options':
|
||||
{'address': {'type': 'str'}},
|
||||
'type': 'list'},
|
||||
'ipv6': {'elements': 'dict',
|
||||
'options':
|
||||
{'address': {'type': 'str'}},
|
||||
'type': 'list'},
|
||||
'name': {'required': True, 'type': 'str'},
|
||||
'unit': {'type': 'int', 'default': 0}
|
||||
},
|
||||
'type': 'list'},
|
||||
'state': {'choices':
|
||||
['merged', 'replaced', 'overridden', 'deleted'],
|
||||
'default': 'merged',
|
||||
'type': 'str'}} # pylint: disable=C0301
|
@ -1,47 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The arg spec for the junos_lacp module
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class LacpArgs(object):
|
||||
"""The arg spec for the junos_lacp module
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
argument_spec = {'config': {'type': 'dict',
|
||||
'options': {'link_protection': {'choices': ['revertive',
|
||||
'non-revertive'],
|
||||
'type': 'str'},
|
||||
'system_priority': {'type': 'int'}},
|
||||
},
|
||||
'state': {'choices': ['merged', 'replaced', 'deleted'],
|
||||
'default': 'merged',
|
||||
'type': 'str'}}
|
@ -1,53 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The arg spec for the junos_lacp_interfaces module
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class Lacp_interfacesArgs(object):
|
||||
"""The arg spec for the junos_lacp_interfaces module
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
argument_spec = {'config': {'elements': 'dict',
|
||||
'options': {'force_up': {'type': 'bool'},
|
||||
'name': {'type': 'str'},
|
||||
'period': {'choices': ['fast', 'slow']},
|
||||
'port_priority': {'type': 'int'},
|
||||
'sync_reset': {'choices': ['disable', 'enable'],
|
||||
'type': 'str'},
|
||||
'system': {'options': {'mac': {'type': 'dict',
|
||||
'options': {'address': {'type': 'str'}}},
|
||||
'priority': {'type': 'int'}},
|
||||
'type': 'dict'}},
|
||||
'type': 'list'},
|
||||
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
|
||||
'default': 'merged',
|
||||
'type': 'str'}}
|
@ -1,51 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The arg spec for the junos_lag_interfaces module
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class Lag_interfacesArgs(object):
|
||||
"""The arg spec for the junos_lag_interfaces module
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
argument_spec = {'config': {'elements': 'dict',
|
||||
'options': {'members': {'elements': 'dict',
|
||||
'options': {'link_type': {'choices': ['primary',
|
||||
'backup']},
|
||||
'member': {'type': 'str'}},
|
||||
'type': 'list'},
|
||||
'mode': {'choices': ['active', 'passive']},
|
||||
'name': {'required': True, 'type': 'str'},
|
||||
'link_protection': {'type': 'bool'}},
|
||||
'type': 'list'},
|
||||
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
|
||||
'default': 'merged',
|
||||
'type': 'str'}}
|
@ -1,47 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The arg spec for the junos_lldp module
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class Lldp_globalArgs(object):
|
||||
"""The arg spec for the junos_lldp module
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
argument_spec = {'config': {'options': {'address': {'type': 'str'},
|
||||
'enabled': {'type': 'bool'},
|
||||
'hold_multiplier': {'type': 'int'},
|
||||
'interval': {'type': 'int'},
|
||||
'transmit_delay': {'type': 'int'}},
|
||||
'type': 'dict'},
|
||||
'state': {'choices': ['merged', 'replaced', 'deleted'],
|
||||
'default': 'merged',
|
||||
'type': 'str'}}
|
@ -1,45 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The arg spec for the junos_lldp_interfaces module
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class Lldp_interfacesArgs(object):
|
||||
"""The arg spec for the junos_lldp_interfaces module
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
argument_spec = {'config': {'elements': 'dict',
|
||||
'options': {'enabled': {'type': 'bool'},
|
||||
'name': {'required': True, 'type': 'str'}},
|
||||
'type': 'list'},
|
||||
'state': {'choices': ['merged', 'replaced', 'deleted', 'overridden'],
|
||||
'default': 'merged',
|
||||
'type': 'str'}}
|
@ -1,73 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The arg spec for the junos_static_routes module
|
||||
"""
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class Static_routesArgs(object): # pylint: disable=R0903
|
||||
"""The arg spec for the junos_static_routes module
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
argument_spec = {
|
||||
'config': {
|
||||
'elements': 'dict',
|
||||
'options': {
|
||||
'address_families': {
|
||||
'elements': 'dict',
|
||||
'options': {
|
||||
'afi': {
|
||||
'choices': ['ipv4', 'ipv6'],
|
||||
'required': True,
|
||||
'type': 'str'},
|
||||
'routes': {'elements': 'dict',
|
||||
'options': {
|
||||
'dest': {
|
||||
'type': 'str'},
|
||||
'metric': {
|
||||
'type': 'int'},
|
||||
'next_hop': {
|
||||
'elements': 'dict',
|
||||
'options': {
|
||||
'forward_router_address': {
|
||||
'type': 'str'}},
|
||||
'type': 'list'}},
|
||||
'type': 'list'}},
|
||||
'type': 'list'},
|
||||
'vrf': {
|
||||
'type': 'str'}},
|
||||
'type': 'list'},
|
||||
'state': {
|
||||
'choices': ['merged',
|
||||
'replaced',
|
||||
'overridden',
|
||||
'deleted'],
|
||||
'default': 'merged',
|
||||
'type': 'str'}} # pylint: disable=C0301
|
@ -1,49 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The arg spec for the junos_vlans module
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class VlansArgs(object): # pylint: disable=R0903
|
||||
"""The arg spec for the junos_vlans module
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
argument_spec = {'config': {'elements': 'dict',
|
||||
'options': {
|
||||
'description': {},
|
||||
'name': {'required': True, 'type': 'str'},
|
||||
'vlan_id': {'type': 'int'}},
|
||||
'type': 'list'},
|
||||
'state': {
|
||||
'choices': ['merged', 'replaced', 'overridden',
|
||||
'deleted'],
|
||||
'default': 'merged',
|
||||
'type': 'str'}} # pylint: disable=C0301
|
@ -1,238 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos_interfaces class
|
||||
It is in this file where the current configuration (as dict)
|
||||
is compared to the provided configuration (as dict) and the command set
|
||||
necessary to bring the current configuration to it's desired end-state is
|
||||
created
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.junos.junos import locked_config, load_config, commit_configuration, discard_changes, tostring
|
||||
from ansible.module_utils.network.junos.facts.facts import Facts
|
||||
from ansible.module_utils.network.common.netconf import build_root_xml_node, build_child_xml_node
|
||||
|
||||
|
||||
class Interfaces(ConfigBase):
|
||||
"""
|
||||
The junos_interfaces class
|
||||
"""
|
||||
|
||||
gather_subset = [
|
||||
'!all',
|
||||
'!min',
|
||||
]
|
||||
|
||||
gather_network_resources = [
|
||||
'interfaces',
|
||||
]
|
||||
|
||||
def __init__(self, module):
|
||||
super(Interfaces, self).__init__(module)
|
||||
|
||||
def get_interfaces_facts(self):
|
||||
""" Get the 'facts' (the current configuration)
|
||||
|
||||
:rtype: A dictionary
|
||||
:returns: The current configuration as a dictionary
|
||||
"""
|
||||
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
|
||||
interfaces_facts = facts['ansible_network_resources'].get('interfaces')
|
||||
if not interfaces_facts:
|
||||
return []
|
||||
return interfaces_facts
|
||||
|
||||
def execute_module(self):
|
||||
""" Execute the module
|
||||
|
||||
:rtype: A dictionary
|
||||
:returns: The result from module execution
|
||||
"""
|
||||
result = {'changed': False}
|
||||
|
||||
existing_interfaces_facts = self.get_interfaces_facts()
|
||||
|
||||
config_xmls = self.set_config(existing_interfaces_facts)
|
||||
with locked_config(self._module):
|
||||
for config_xml in to_list(config_xmls):
|
||||
diff = load_config(self._module, config_xml, [])
|
||||
|
||||
commit = not self._module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(self._module)
|
||||
else:
|
||||
discard_changes(self._module)
|
||||
result['changed'] = True
|
||||
|
||||
if self._module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
result['commands'] = config_xmls
|
||||
|
||||
changed_interfaces_facts = self.get_interfaces_facts()
|
||||
|
||||
result['before'] = existing_interfaces_facts
|
||||
if result['changed']:
|
||||
result['after'] = changed_interfaces_facts
|
||||
|
||||
return result
|
||||
|
||||
def set_config(self, existing_interfaces_facts):
|
||||
""" Collect the configuration from the args passed to the module,
|
||||
collect the current configuration (as a dict from facts)
|
||||
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
want = self._module.params['config']
|
||||
have = existing_interfaces_facts
|
||||
resp = self.set_state(want, have)
|
||||
return to_list(resp)
|
||||
|
||||
def set_state(self, want, have):
|
||||
""" Select the appropriate function based on the state provided
|
||||
|
||||
:param want: the desired configuration as a dictionary
|
||||
:param have: the current configuration as a dictionary
|
||||
:rtype: A list
|
||||
:returns: the list xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
root = build_root_xml_node('interfaces')
|
||||
state = self._module.params['state']
|
||||
if state == 'overridden':
|
||||
config_xmls = self._state_overridden(want, have)
|
||||
elif state == 'deleted':
|
||||
config_xmls = self._state_deleted(want, have)
|
||||
elif state == 'merged':
|
||||
config_xmls = self._state_merged(want, have)
|
||||
elif state == 'replaced':
|
||||
config_xmls = self._state_replaced(want, have)
|
||||
|
||||
for xml in config_xmls:
|
||||
root.append(xml)
|
||||
|
||||
return tostring(root)
|
||||
|
||||
def _state_replaced(self, want, have):
|
||||
""" The xml configuration generator when state is replaced
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
intf_xml.extend(self._state_deleted(want, have))
|
||||
intf_xml.extend(self._state_merged(want, have))
|
||||
|
||||
return intf_xml
|
||||
|
||||
def _state_overridden(self, want, have):
|
||||
""" The xml configuration generator when state is overridden
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
interface_xmls_obj = []
|
||||
# replace interface config with data in want
|
||||
interface_xmls_obj.extend(self._state_replaced(want, have))
|
||||
|
||||
# delete interface config if interface in have not present in want
|
||||
delete_obj = []
|
||||
for have_obj in have:
|
||||
for want_obj in want:
|
||||
if have_obj['name'] == want_obj['name']:
|
||||
break
|
||||
else:
|
||||
delete_obj.append(have_obj)
|
||||
|
||||
if delete_obj:
|
||||
interface_xmls_obj.extend(self._state_deleted(delete_obj, have))
|
||||
return interface_xmls_obj
|
||||
|
||||
def _state_merged(self, want, have):
|
||||
""" The xml configuration generator when state is merged
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to merge the provided into
|
||||
the current configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
|
||||
for config in want:
|
||||
intf = build_root_xml_node('interface')
|
||||
build_child_xml_node(intf, 'name', config['name'])
|
||||
|
||||
intf_fields = ['description', 'speed']
|
||||
if not config['name'].startswith('fxp'):
|
||||
intf_fields.append('mtu')
|
||||
for field in intf_fields:
|
||||
if config.get(field):
|
||||
build_child_xml_node(intf, field, config[field])
|
||||
|
||||
if config.get('duplex'):
|
||||
build_child_xml_node(intf, 'link-mode', config['duplex'])
|
||||
|
||||
if config.get('enabled') is False:
|
||||
build_child_xml_node(intf, 'disable')
|
||||
|
||||
holdtime = config.get('hold_time')
|
||||
if holdtime:
|
||||
holdtime_ele = build_child_xml_node(intf, 'hold-time')
|
||||
|
||||
for holdtime_field in ['up', 'down']:
|
||||
build_child_xml_node(holdtime_ele, holdtime_field, holdtime.get(holdtime_field, ''))
|
||||
intf_xml.append(intf)
|
||||
|
||||
return intf_xml
|
||||
|
||||
def _state_deleted(self, want, have):
|
||||
""" The xml configuration generator when state is deleted
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to remove the current configuration
|
||||
of the provided objects
|
||||
"""
|
||||
intf_xml = []
|
||||
intf_obj = want
|
||||
|
||||
if not intf_obj:
|
||||
# delete base interfaces attribute from all the existing interface
|
||||
intf_obj = have
|
||||
|
||||
for config in intf_obj:
|
||||
intf = build_root_xml_node('interface')
|
||||
build_child_xml_node(intf, 'name', config['name'])
|
||||
|
||||
intf_fields = ['description']
|
||||
if not config['name'].startswith('lo'):
|
||||
intf_fields.append('speed')
|
||||
|
||||
if not any([config['name'].startswith('fxp'), config['name'].startswith('lo')]):
|
||||
intf_fields.append('mtu')
|
||||
|
||||
for field in intf_fields:
|
||||
build_child_xml_node(intf, field, None, {'delete': 'delete'})
|
||||
|
||||
if not config['name'].startswith('lo'):
|
||||
build_child_xml_node(intf, 'link-mode', None, {'delete': 'delete'})
|
||||
|
||||
build_child_xml_node(intf, 'disable', None, {'delete': 'delete'})
|
||||
|
||||
holdtime_ele = build_child_xml_node(intf, 'hold-time')
|
||||
for holdtime_field in ['up', 'down']:
|
||||
build_child_xml_node(holdtime_ele, holdtime_field, None, {'delete': 'delete'})
|
||||
intf_xml.append(intf)
|
||||
|
||||
return intf_xml
|
@ -1,256 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos_l2_interfaces class
|
||||
It is in this file where the current configuration (as dict)
|
||||
is compared to the provided configuration (as dict) and the command set
|
||||
necessary to bring the current configuration to it's desired end-state is
|
||||
created
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.junos.junos import locked_config, load_config, commit_configuration, discard_changes, tostring
|
||||
from ansible.module_utils.network.junos.facts.facts import Facts
|
||||
from ansible.module_utils.network.junos.utils.utils import get_resource_config
|
||||
from ansible.module_utils.network.common.netconf import build_root_xml_node, build_child_xml_node, build_subtree
|
||||
|
||||
|
||||
class L2_interfaces(ConfigBase):
|
||||
"""
|
||||
The junos_l2_interfaces class
|
||||
"""
|
||||
|
||||
gather_subset = [
|
||||
'!all',
|
||||
'!min',
|
||||
]
|
||||
|
||||
gather_network_resources = [
|
||||
'l2_interfaces',
|
||||
]
|
||||
|
||||
def __init__(self, module):
|
||||
super(L2_interfaces, self).__init__(module)
|
||||
|
||||
def get_l2_interfaces_facts(self):
|
||||
""" Get the 'facts' (the current configuration)
|
||||
|
||||
:rtype: A dictionary
|
||||
:returns: The current configuration as a dictionary
|
||||
"""
|
||||
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
|
||||
l2_interfaces_facts = facts['ansible_network_resources'].get('l2_interfaces')
|
||||
if not l2_interfaces_facts:
|
||||
return []
|
||||
return l2_interfaces_facts
|
||||
|
||||
def execute_module(self):
|
||||
""" Execute the module
|
||||
|
||||
:rtype: A dictionary
|
||||
:returns: The result from module execution
|
||||
"""
|
||||
result = {'changed': False}
|
||||
|
||||
existing_l2_interfaces_facts = self.get_l2_interfaces_facts()
|
||||
|
||||
config_xmls = self.set_config(existing_l2_interfaces_facts)
|
||||
with locked_config(self._module):
|
||||
for config_xml in to_list(config_xmls):
|
||||
diff = load_config(self._module, config_xml, [])
|
||||
|
||||
commit = not self._module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(self._module)
|
||||
else:
|
||||
discard_changes(self._module)
|
||||
result['changed'] = True
|
||||
|
||||
if self._module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
result['commands'] = config_xmls
|
||||
|
||||
changed_l2_interfaces_facts = self.get_l2_interfaces_facts()
|
||||
|
||||
result['before'] = existing_l2_interfaces_facts
|
||||
if result['changed']:
|
||||
result['after'] = changed_l2_interfaces_facts
|
||||
|
||||
return result
|
||||
|
||||
def set_config(self, existing_l2_interfaces_facts):
|
||||
""" Collect the configuration from the args passed to the module,
|
||||
collect the current configuration (as a dict from facts)
|
||||
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
want = self._module.params['config']
|
||||
have = existing_l2_interfaces_facts
|
||||
resp = self.set_state(want, have)
|
||||
return to_list(resp)
|
||||
|
||||
def set_state(self, want, have):
|
||||
""" Select the appropriate function based on the state provided
|
||||
|
||||
:param want: the desired configuration as a dictionary
|
||||
:param have: the current configuration as a dictionary
|
||||
:rtype: A list
|
||||
:returns: the list xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
root = build_root_xml_node('interfaces')
|
||||
state = self._module.params['state']
|
||||
if state == 'overridden':
|
||||
config_xmls = self._state_overridden(want, have)
|
||||
elif state == 'deleted':
|
||||
config_xmls = self._state_deleted(want, have)
|
||||
elif state == 'merged':
|
||||
config_xmls = self._state_merged(want, have)
|
||||
elif state == 'replaced':
|
||||
config_xmls = self._state_replaced(want, have)
|
||||
|
||||
for xml in config_xmls:
|
||||
root.append(xml)
|
||||
|
||||
return tostring(root)
|
||||
|
||||
def _state_replaced(self, want, have):
|
||||
""" The xml configuration generator when state is replaced
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
l2_intf_xml = []
|
||||
l2_intf_xml.extend(self._state_deleted(want, have))
|
||||
l2_intf_xml.extend(self._state_merged(want, have))
|
||||
|
||||
return l2_intf_xml
|
||||
|
||||
def _state_overridden(self, want, have):
|
||||
""" The xml configuration generator when state is overridden
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
l2_interface_xmls_obj = []
|
||||
# replace interface config with data in want
|
||||
l2_interface_xmls_obj.extend(self._state_replaced(want, have))
|
||||
|
||||
# delete interface config if interface in have not present in want
|
||||
delete_obj = []
|
||||
for have_obj in have:
|
||||
for want_obj in want:
|
||||
if have_obj['name'] == want_obj['name']:
|
||||
break
|
||||
else:
|
||||
delete_obj.append(have_obj)
|
||||
|
||||
if delete_obj:
|
||||
l2_interface_xmls_obj.extend(self._state_deleted(delete_obj, have))
|
||||
return l2_interface_xmls_obj
|
||||
|
||||
def _state_merged(self, want, have):
|
||||
""" The xml configuration generator when state is merged
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to merge the provided into
|
||||
the current configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
for config in want:
|
||||
enhanced_layer = True
|
||||
if config.get('enhanced_layer') is False:
|
||||
enhanced_layer = False
|
||||
|
||||
mode = 'interface-mode' if enhanced_layer else 'port-mode'
|
||||
intf = build_root_xml_node('interface')
|
||||
build_child_xml_node(intf, 'name', config['name'])
|
||||
unit_node = build_child_xml_node(intf, 'unit')
|
||||
unit = config['unit'] if config['unit'] else '0'
|
||||
build_child_xml_node(unit_node, 'name', unit)
|
||||
|
||||
eth_node = build_subtree(unit_node, 'family/ethernet-switching')
|
||||
if config.get('access'):
|
||||
vlan = config['access'].get('vlan')
|
||||
if vlan:
|
||||
build_child_xml_node(eth_node, mode, 'access')
|
||||
vlan_node = build_child_xml_node(eth_node, 'vlan')
|
||||
build_child_xml_node(vlan_node, 'members', vlan)
|
||||
intf_xml.append(intf)
|
||||
elif config.get('trunk'):
|
||||
allowed_vlans = config['trunk'].get('allowed_vlans')
|
||||
native_vlan = config['trunk'].get('native_vlan')
|
||||
if allowed_vlans:
|
||||
build_child_xml_node(eth_node, mode, 'trunk')
|
||||
vlan_node = build_child_xml_node(eth_node, 'vlan')
|
||||
for vlan in allowed_vlans:
|
||||
build_child_xml_node(vlan_node, 'members', vlan)
|
||||
if native_vlan:
|
||||
build_child_xml_node(intf, 'native-vlan-id', native_vlan)
|
||||
|
||||
if allowed_vlans or native_vlan:
|
||||
intf_xml.append(intf)
|
||||
|
||||
return intf_xml
|
||||
|
||||
def _state_deleted(self, want, have):
|
||||
""" The xml configuration generator when state is deleted
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to remove the current configuration
|
||||
of the provided objects
|
||||
"""
|
||||
l2_intf_xml = []
|
||||
l2_intf_obj = want
|
||||
|
||||
config_filter = """
|
||||
<configuration>
|
||||
<interfaces/>
|
||||
</configuration>
|
||||
"""
|
||||
data = get_resource_config(self._connection, config_filter=config_filter)
|
||||
|
||||
if not l2_intf_obj:
|
||||
# delete l2 interfaces attribute from all the existing interface having l2 config
|
||||
l2_intf_obj = have
|
||||
|
||||
for config in l2_intf_obj:
|
||||
name = config['name']
|
||||
enhanced_layer = True
|
||||
l2_mode = data.xpath("configuration/interfaces/interface[name='%s']/unit/family/ethernet-switching/interface-mode" % name)
|
||||
|
||||
if not len(l2_mode):
|
||||
l2_mode = data.xpath("configuration/interfaces/interface[name='%s']/unit/family/ethernet-switching/port-mode" % name)
|
||||
enhanced_layer = False
|
||||
|
||||
if len(l2_mode):
|
||||
mode = 'interface-mode' if enhanced_layer else 'port-mode'
|
||||
|
||||
intf = build_root_xml_node('interface')
|
||||
build_child_xml_node(intf, 'name', name)
|
||||
|
||||
unit_node = build_child_xml_node(intf, 'unit')
|
||||
unit = config['unit'] if config['unit'] else '0'
|
||||
build_child_xml_node(unit_node, 'name', unit)
|
||||
|
||||
eth_node = build_subtree(unit_node, 'family/ethernet-switching')
|
||||
build_child_xml_node(eth_node, mode, None, {'delete': 'delete'})
|
||||
build_child_xml_node(eth_node, 'vlan', None, {'delete': 'delete'})
|
||||
build_child_xml_node(intf, 'native-vlan-id', None, {'delete': 'delete'})
|
||||
|
||||
l2_intf_xml.append(intf)
|
||||
|
||||
return l2_intf_xml
|
@ -1,234 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos_l3_interfaces class
|
||||
It is in this file where the current configuration (as dict)
|
||||
is compared to the provided configuration (as dict) and the command set
|
||||
necessary to bring the current configuration to it's desired end-state is
|
||||
created
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.module_utils.network.junos.facts.facts import Facts
|
||||
from ansible.module_utils.network.junos.junos import (
|
||||
locked_config, load_config, commit_configuration, discard_changes,
|
||||
tostring)
|
||||
from ansible.module_utils.network.common.netconf import (build_root_xml_node,
|
||||
build_child_xml_node)
|
||||
|
||||
|
||||
class L3_interfaces(ConfigBase):
|
||||
"""
|
||||
The junos_l3_interfaces class
|
||||
"""
|
||||
|
||||
gather_subset = [
|
||||
'!all',
|
||||
'!min',
|
||||
]
|
||||
|
||||
gather_network_resources = [
|
||||
'l3_interfaces',
|
||||
]
|
||||
|
||||
def __init__(self, module):
|
||||
super(L3_interfaces, self).__init__(module)
|
||||
|
||||
def get_l3_interfaces_facts(self):
|
||||
""" Get the 'facts' (the current configuration)
|
||||
|
||||
:rtype: A dictionary
|
||||
:returns: The current configuration as a dictionary
|
||||
"""
|
||||
facts, _warnings = Facts(self._module).get_facts(
|
||||
self.gather_subset, self.gather_network_resources)
|
||||
l3_interfaces_facts = facts['ansible_network_resources'].get(
|
||||
'l3_interfaces')
|
||||
if not l3_interfaces_facts:
|
||||
return []
|
||||
return l3_interfaces_facts
|
||||
|
||||
def execute_module(self):
|
||||
""" Execute the module
|
||||
|
||||
:rtype: A dictionary
|
||||
:returns: The result from module execution
|
||||
"""
|
||||
result = {'changed': False}
|
||||
warnings = list()
|
||||
|
||||
existing_interfaces_facts = self.get_l3_interfaces_facts()
|
||||
|
||||
config_xmls = self.set_config(existing_interfaces_facts)
|
||||
with locked_config(self._module):
|
||||
for config_xml in to_list(config_xmls):
|
||||
diff = load_config(self._module, config_xml, warnings)
|
||||
|
||||
commit = not self._module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(self._module)
|
||||
else:
|
||||
discard_changes(self._module)
|
||||
result['changed'] = True
|
||||
|
||||
if self._module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
result['commands'] = config_xmls
|
||||
|
||||
changed_interfaces_facts = self.get_l3_interfaces_facts()
|
||||
|
||||
result['before'] = existing_interfaces_facts
|
||||
if result['changed']:
|
||||
result['after'] = changed_interfaces_facts
|
||||
|
||||
result['warnings'] = warnings
|
||||
return result
|
||||
|
||||
def set_config(self, existing_l3_interfaces_facts):
|
||||
""" Collect the configuration from the args passed to the module,
|
||||
collect the current configuration (as a dict from facts)
|
||||
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
want = self._module.params['config']
|
||||
have = existing_l3_interfaces_facts
|
||||
resp = self.set_state(want, have)
|
||||
return to_list(resp)
|
||||
|
||||
def set_state(self, want, have):
|
||||
""" Select the appropriate function based on the state provided
|
||||
|
||||
:param want: the desired configuration as a dictionary
|
||||
:param have: the current configuration as a dictionary
|
||||
:rtype: A list
|
||||
:returns: the list xml configuration necessary to migrate the current
|
||||
configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
root = build_root_xml_node('interfaces')
|
||||
state = self._module.params['state']
|
||||
if state == 'overridden':
|
||||
config_xmls = self._state_overridden(want, have)
|
||||
elif state == 'deleted':
|
||||
config_xmls = self._state_deleted(want, have)
|
||||
elif state == 'merged':
|
||||
config_xmls = self._state_merged(want, have)
|
||||
elif state == 'replaced':
|
||||
config_xmls = self._state_replaced(want, have)
|
||||
|
||||
for xml in config_xmls:
|
||||
root.append(xml)
|
||||
|
||||
return tostring(root)
|
||||
|
||||
def _get_common_xml_node(self, name):
|
||||
root_node = build_root_xml_node('interface')
|
||||
build_child_xml_node(root_node, 'name', name)
|
||||
intf_unit_node = build_child_xml_node(root_node, 'unit')
|
||||
return root_node, intf_unit_node
|
||||
|
||||
def _state_replaced(self, want, have):
|
||||
""" The xml generator when state is replaced
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
intf_xml.extend(self._state_deleted(want, have))
|
||||
intf_xml.extend(self._state_merged(want, have))
|
||||
return intf_xml
|
||||
|
||||
def _state_overridden(self, want, have):
|
||||
""" The xml generator when state is overridden
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
intf_xml.extend(self._state_deleted(have, have))
|
||||
intf_xml.extend(self._state_merged(want, have))
|
||||
return intf_xml
|
||||
|
||||
def _state_merged(self, want, have):
|
||||
""" The xml generator when state is merged
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml necessary to merge the provided into
|
||||
the current configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
for config in want:
|
||||
root_node, unit_node = self._get_common_xml_node(config['name'])
|
||||
build_child_xml_node(unit_node, 'name',
|
||||
str(config['unit']))
|
||||
if config.get('ipv4'):
|
||||
self.build_ipaddr_et(config, unit_node)
|
||||
if config.get('ipv6'):
|
||||
self.build_ipaddr_et(config, unit_node, protocol='ipv6')
|
||||
intf_xml.append(root_node)
|
||||
return intf_xml
|
||||
|
||||
def build_ipaddr_et(self, config, unit_node, protocol='ipv4',
|
||||
delete=False):
|
||||
family = build_child_xml_node(unit_node, 'family')
|
||||
inet = 'inet'
|
||||
if protocol == 'ipv6':
|
||||
inet = 'inet6'
|
||||
ip_protocol = build_child_xml_node(family, inet)
|
||||
for ip_addr in config[protocol]:
|
||||
if ip_addr['address'] == 'dhcp' and protocol == 'ipv4':
|
||||
build_child_xml_node(ip_protocol, 'dhcp')
|
||||
else:
|
||||
ip_addresses = build_child_xml_node(
|
||||
ip_protocol, 'address')
|
||||
build_child_xml_node(
|
||||
ip_addresses, 'name', ip_addr['address'])
|
||||
|
||||
def _state_deleted(self, want, have):
|
||||
""" The xml configuration generator when state is deleted
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to remove the current
|
||||
configuration of the provided objects
|
||||
"""
|
||||
intf_xml = []
|
||||
existing_l3_intfs = [l3_intf['name'] for l3_intf in have]
|
||||
|
||||
if not want:
|
||||
want = have
|
||||
|
||||
for config in want:
|
||||
if config['name'] not in existing_l3_intfs:
|
||||
continue
|
||||
else:
|
||||
root_node, unit_node = self._get_common_xml_node(
|
||||
config['name'])
|
||||
build_child_xml_node(unit_node, 'name',
|
||||
str(config['unit']))
|
||||
family = build_child_xml_node(unit_node, 'family')
|
||||
ipv4 = build_child_xml_node(family, 'inet')
|
||||
intf = next(
|
||||
(intf for intf in have if intf['name'] == config['name']),
|
||||
None)
|
||||
if 'ipv4' in intf:
|
||||
if 'dhcp' in [x['address'] for x in intf.get('ipv4') if intf.get('ipv4') is not None]:
|
||||
build_child_xml_node(ipv4, 'dhcp', None, {'delete': 'delete'})
|
||||
else:
|
||||
build_child_xml_node(
|
||||
ipv4, 'address', None, {'delete': 'delete'})
|
||||
ipv6 = build_child_xml_node(family, 'inet6')
|
||||
build_child_xml_node(ipv6, 'address', None, {'delete': 'delete'})
|
||||
intf_xml.append(root_node)
|
||||
return intf_xml
|
@ -1,185 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos_lacp class
|
||||
It is in this file where the current configuration (as dict)
|
||||
is compared to the provided configuration (as dict) and the command set
|
||||
necessary to bring the current configuration to it's desired end-state is
|
||||
created
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.common.netconf import build_root_xml_node, build_child_xml_node, build_subtree
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.module_utils.network.junos.facts.facts import Facts
|
||||
from ansible.module_utils.network.junos.junos import locked_config, load_config, commit_configuration, discard_changes, tostring
|
||||
|
||||
|
||||
class Lacp(ConfigBase):
|
||||
"""
|
||||
The junos_lacp class
|
||||
"""
|
||||
|
||||
gather_subset = [
|
||||
'!all',
|
||||
'!min',
|
||||
]
|
||||
|
||||
gather_network_resources = [
|
||||
'lacp',
|
||||
]
|
||||
|
||||
def __init__(self, module):
|
||||
super(Lacp, self).__init__(module)
|
||||
|
||||
def get_lacp_facts(self):
|
||||
""" Get the 'facts' (the current configuration)
|
||||
|
||||
:rtype: A dictionary
|
||||
:returns: The current configuration as a dictionary
|
||||
"""
|
||||
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
|
||||
lacp_facts = facts['ansible_network_resources'].get('lacp')
|
||||
if not lacp_facts:
|
||||
return {}
|
||||
return lacp_facts
|
||||
|
||||
def execute_module(self):
|
||||
""" Execute the module
|
||||
|
||||
:rtype: A dictionary
|
||||
:returns: The result from module execution
|
||||
"""
|
||||
result = {'changed': False}
|
||||
|
||||
existing_lacp_facts = self.get_lacp_facts()
|
||||
config_xmls = self.set_config(existing_lacp_facts)
|
||||
|
||||
with locked_config(self._module):
|
||||
for config_xml in to_list(config_xmls):
|
||||
diff = load_config(self._module, config_xml, [])
|
||||
|
||||
commit = not self._module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(self._module)
|
||||
else:
|
||||
discard_changes(self._module)
|
||||
result['changed'] = True
|
||||
|
||||
if self._module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
result['commands'] = config_xmls
|
||||
|
||||
changed_lacp_facts = self.get_lacp_facts()
|
||||
|
||||
result['before'] = existing_lacp_facts
|
||||
if result['changed']:
|
||||
result['after'] = changed_lacp_facts
|
||||
|
||||
return result
|
||||
|
||||
def set_config(self, existing_lacp_facts):
|
||||
""" Collect the configuration from the args passed to the module,
|
||||
collect the current configuration (as a dict from facts)
|
||||
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
want = self._module.params['config']
|
||||
have = existing_lacp_facts
|
||||
resp = self.set_state(want, have)
|
||||
return to_list(resp)
|
||||
|
||||
def set_state(self, want, have):
|
||||
""" Select the appropriate function based on the state provided
|
||||
:param want: the desired configuration as a dictionary
|
||||
:param have: the current configuration as a dictionary
|
||||
:rtype: A list
|
||||
:returns: the list xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
root = build_root_xml_node('chassis')
|
||||
ethernet_ele = build_subtree(root, 'aggregated-devices/ethernet')
|
||||
state = self._module.params['state']
|
||||
if state == 'overridden':
|
||||
config_xmls = self._state_overridden(want, have)
|
||||
elif state == 'deleted':
|
||||
config_xmls = self._state_deleted(want, have)
|
||||
elif state == 'merged':
|
||||
config_xmls = self._state_merged(want, have)
|
||||
elif state == 'replaced':
|
||||
config_xmls = self._state_replaced(want, have)
|
||||
|
||||
for xml in config_xmls:
|
||||
ethernet_ele.append(xml)
|
||||
return tostring(root)
|
||||
|
||||
def _state_replaced(self, want, have):
|
||||
""" The xml configuration generator when state is merged
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to merge the provided into
|
||||
the current configuration
|
||||
"""
|
||||
lacp_xml = []
|
||||
lacp_xml.extend(self._state_deleted(want, have))
|
||||
lacp_xml.extend(self._state_merged(want, have))
|
||||
|
||||
return lacp_xml
|
||||
|
||||
def _state_overridden(self, want, have):
|
||||
""" The command generator when state is overridden
|
||||
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
lacp_xml = []
|
||||
lacp_xml.extend(self._state_deleted(want, have))
|
||||
lacp_xml.extend(self._state_merged(want, have))
|
||||
|
||||
return lacp_xml
|
||||
|
||||
def _state_merged(self, want, have):
|
||||
""" Select the appropriate function based on the state provided
|
||||
:param want: the desired configuration as a dictionary
|
||||
:param have: the current configuration as a dictionary
|
||||
:rtype: A list
|
||||
:returns: the list xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
lacp_xml = []
|
||||
|
||||
lacp_root = build_root_xml_node('lacp')
|
||||
build_child_xml_node(lacp_root, 'system-priority', want.get('system_priority'))
|
||||
if want.get('link_protection') == 'non-revertive':
|
||||
build_subtree(lacp_root, 'link-protection/non-revertive')
|
||||
elif want.get('link_protection') == 'revertive':
|
||||
link_root = build_child_xml_node(lacp_root, 'link-protection')
|
||||
build_child_xml_node(link_root, 'non-revertive', None, {'delete': 'delete'})
|
||||
lacp_xml.append(lacp_root)
|
||||
return lacp_xml
|
||||
|
||||
def _state_deleted(self, want, have):
|
||||
""" The command generator when state is deleted
|
||||
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to remove the current configuration
|
||||
of the provided objects
|
||||
"""
|
||||
lacp_xml = []
|
||||
|
||||
lacp_root = build_root_xml_node('lacp')
|
||||
build_child_xml_node(lacp_root, 'system-priority', None, {'delete': 'delete'})
|
||||
element = build_child_xml_node(lacp_root, 'link-protection', None, {'delete': 'delete'})
|
||||
build_child_xml_node(element, 'non-revertive', None, {'delete': 'delete'})
|
||||
|
||||
lacp_xml.append(lacp_root)
|
||||
return lacp_xml
|
@ -1,227 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos_lacp_interfaces class
|
||||
It is in this file where the current configuration (as dict)
|
||||
is compared to the provided configuration (as dict) and the command set
|
||||
necessary to bring the current configuration to it's desired end-state is
|
||||
created
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.module_utils.network.junos.facts.facts import Facts
|
||||
from ansible.module_utils.network.junos.junos import locked_config, load_config, commit_configuration, discard_changes, tostring
|
||||
from ansible.module_utils.network.common.netconf import build_root_xml_node, build_child_xml_node, build_subtree
|
||||
|
||||
|
||||
class Lacp_interfaces(ConfigBase):
|
||||
"""
|
||||
The junos_lacp_interfaces class
|
||||
"""
|
||||
|
||||
gather_subset = [
|
||||
'!all',
|
||||
'!min',
|
||||
]
|
||||
|
||||
gather_network_resources = [
|
||||
'lacp_interfaces',
|
||||
]
|
||||
|
||||
def __init__(self, module):
|
||||
super(Lacp_interfaces, self).__init__(module)
|
||||
|
||||
def get_lacp_interfaces_facts(self):
|
||||
""" Get the 'facts' (the current configuration)
|
||||
:rtype: A dictionary
|
||||
:returns: The current configuration as a dictionary
|
||||
"""
|
||||
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
|
||||
lacp_interfaces_facts = facts['ansible_network_resources'].get('lacp_interfaces')
|
||||
if not lacp_interfaces_facts:
|
||||
return []
|
||||
return lacp_interfaces_facts
|
||||
|
||||
def execute_module(self):
|
||||
""" Execute the module
|
||||
:rtype: A dictionary
|
||||
:returns: The result from module execution
|
||||
"""
|
||||
result = {'changed': False}
|
||||
|
||||
existing_lacp_interfaces_facts = self.get_lacp_interfaces_facts()
|
||||
config_xmls = self.set_config(existing_lacp_interfaces_facts)
|
||||
|
||||
with locked_config(self._module):
|
||||
for config_xml in to_list(config_xmls):
|
||||
diff = load_config(self._module, config_xml, [])
|
||||
|
||||
commit = not self._module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(self._module)
|
||||
else:
|
||||
discard_changes(self._module)
|
||||
result['changed'] = True
|
||||
|
||||
if self._module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
result['commands'] = config_xmls
|
||||
|
||||
changed_lacp_interfaces_facts = self.get_lacp_interfaces_facts()
|
||||
|
||||
result['before'] = existing_lacp_interfaces_facts
|
||||
if result['changed']:
|
||||
result['after'] = changed_lacp_interfaces_facts
|
||||
|
||||
return result
|
||||
|
||||
def set_config(self, existing_lacp_interfaces_facts):
|
||||
""" Collect the configuration from the args passed to the module,
|
||||
collect the current configuration (as a dict from facts)
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
want = self._module.params['config']
|
||||
have = existing_lacp_interfaces_facts
|
||||
resp = self.set_state(want, have)
|
||||
return to_list(resp)
|
||||
|
||||
def set_state(self, want, have):
|
||||
""" Select the appropriate function based on the state provided
|
||||
:param want: the desired configuration as a dictionary
|
||||
:param have: the current configuration as a dictionary
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
root = build_root_xml_node('interfaces')
|
||||
state = self._module.params['state']
|
||||
if state == 'overridden':
|
||||
config_xmls = self._state_overridden(want, have)
|
||||
elif state == 'deleted':
|
||||
config_xmls = self._state_deleted(want, have)
|
||||
elif state == 'merged':
|
||||
config_xmls = self._state_merged(want, have)
|
||||
elif state == 'replaced':
|
||||
config_xmls = self._state_replaced(want, have)
|
||||
|
||||
for xml in config_xmls:
|
||||
root.append(xml)
|
||||
|
||||
return tostring(root)
|
||||
|
||||
def _state_replaced(self, want, have):
|
||||
""" The xml configuration generator when state is replaced
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
intf_xml.extend(self._state_deleted(want, have))
|
||||
intf_xml.extend(self._state_merged(want, have))
|
||||
|
||||
return intf_xml
|
||||
|
||||
def _state_overridden(self, want, have):
|
||||
""" The xml configuration generator when state is overridden
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
interface_xmls_obj = []
|
||||
# replace interface config with data in want
|
||||
interface_xmls_obj.extend(self._state_replaced(want, have))
|
||||
|
||||
# delete interface config if interface in have not present in want
|
||||
delete_obj = []
|
||||
for have_obj in have:
|
||||
for want_obj in want:
|
||||
if have_obj['name'] == want_obj['name']:
|
||||
break
|
||||
else:
|
||||
delete_obj.append(have_obj)
|
||||
|
||||
if delete_obj:
|
||||
interface_xmls_obj.extend(self._state_deleted(delete_obj, have))
|
||||
return interface_xmls_obj
|
||||
|
||||
def _state_merged(self, want, have):
|
||||
""" The xml configuration generator when state is merged
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to merge the provided into
|
||||
the current configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
|
||||
for config in want:
|
||||
lacp_intf_name = config['name']
|
||||
lacp_intf_root = build_root_xml_node('interface')
|
||||
|
||||
build_child_xml_node(lacp_intf_root, 'name', lacp_intf_name)
|
||||
if lacp_intf_name.startswith('ae'):
|
||||
element = build_subtree(lacp_intf_root, 'aggregated-ether-options/lacp')
|
||||
if config['period']:
|
||||
build_child_xml_node(element, 'periodic', config['period'])
|
||||
if config['sync_reset']:
|
||||
build_child_xml_node(element, 'sync-reset', config['sync_reset'])
|
||||
|
||||
system = config['system']
|
||||
if system:
|
||||
mac = system.get('mac')
|
||||
if mac:
|
||||
if mac.get('address'):
|
||||
build_child_xml_node(element, 'system-id', mac['address'])
|
||||
if system.get('priority'):
|
||||
build_child_xml_node(element, 'system-priority', system['priority'])
|
||||
intf_xml.append(lacp_intf_root)
|
||||
elif config['port_priority'] or config['force_up'] is not None:
|
||||
element = build_subtree(lacp_intf_root, 'ether-options/ieee-802.3ad/lacp')
|
||||
build_child_xml_node(element, 'port-priority', config['port_priority'])
|
||||
if config['force_up'] is False:
|
||||
build_child_xml_node(element, 'force-up', None, {'delete': 'delete'})
|
||||
else:
|
||||
build_child_xml_node(element, 'force-up')
|
||||
intf_xml.append(lacp_intf_root)
|
||||
|
||||
return intf_xml
|
||||
|
||||
def _state_deleted(self, want, have):
|
||||
""" The xml configuration generator when state is deleted
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to remove the current configuration
|
||||
of the provided objects
|
||||
"""
|
||||
intf_xml = []
|
||||
intf_obj = want
|
||||
|
||||
if not intf_obj:
|
||||
# delete lag interfaces attribute for all the interface
|
||||
intf_obj = have
|
||||
|
||||
for config in intf_obj:
|
||||
lacp_intf_name = config['name']
|
||||
lacp_intf_root = build_root_xml_node('interface')
|
||||
build_child_xml_node(lacp_intf_root, 'name', lacp_intf_name)
|
||||
if lacp_intf_name.startswith('ae'):
|
||||
element = build_subtree(lacp_intf_root, 'aggregated-ether-options/lacp')
|
||||
build_child_xml_node(element, 'periodic', None, {'delete': 'delete'})
|
||||
build_child_xml_node(element, 'sync-reset', None, {'delete': 'delete'})
|
||||
build_child_xml_node(element, 'system-id', None, {'delete': 'delete'})
|
||||
build_child_xml_node(element, 'system-priority', None, {'delete': 'delete'})
|
||||
else:
|
||||
element = build_subtree(lacp_intf_root, 'ether-options/ieee-802.3ad/lacp')
|
||||
build_child_xml_node(element, 'port-priority', None, {'delete': 'delete'})
|
||||
build_child_xml_node(element, 'force-up', None, {'delete': 'delete'})
|
||||
|
||||
intf_xml.append(lacp_intf_root)
|
||||
|
||||
return intf_xml
|
@ -1,253 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos_lag_interfaces class
|
||||
It is in this file where the current configuration (as dict)
|
||||
is compared to the provided configuration (as dict) and the command set
|
||||
necessary to bring the current configuration to it's desired end-state is
|
||||
created
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.module_utils.network.junos.facts.facts import Facts
|
||||
from ansible.module_utils.network.junos.junos import locked_config, load_config, commit_configuration, discard_changes, tostring
|
||||
from ansible.module_utils.network.junos.utils.utils import get_resource_config
|
||||
from ansible.module_utils.network.common.netconf import build_root_xml_node, build_child_xml_node, build_subtree
|
||||
|
||||
|
||||
class Lag_interfaces(ConfigBase):
|
||||
"""
|
||||
The junos_lag_interfaces class
|
||||
"""
|
||||
|
||||
gather_subset = [
|
||||
'!all',
|
||||
'!min',
|
||||
]
|
||||
|
||||
gather_network_resources = [
|
||||
'lag_interfaces',
|
||||
]
|
||||
|
||||
def __init__(self, module):
|
||||
super(Lag_interfaces, self).__init__(module)
|
||||
|
||||
def get_lag_interfaces_facts(self):
|
||||
""" Get the 'facts' (the current configuration)
|
||||
:rtype: A dictionary
|
||||
:returns: The current configuration as a dictionary
|
||||
"""
|
||||
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
|
||||
lag_interfaces_facts = facts['ansible_network_resources'].get('lag_interfaces')
|
||||
if not lag_interfaces_facts:
|
||||
return []
|
||||
return lag_interfaces_facts
|
||||
|
||||
def execute_module(self):
|
||||
""" Execute the module
|
||||
:rtype: A dictionary
|
||||
:returns: The result from module execution
|
||||
"""
|
||||
result = {'changed': False}
|
||||
warnings = list()
|
||||
|
||||
existing_lag_interfaces_facts = self.get_lag_interfaces_facts()
|
||||
config_xmls = self.set_config(existing_lag_interfaces_facts)
|
||||
|
||||
with locked_config(self._module):
|
||||
for config_xml in to_list(config_xmls):
|
||||
diff = load_config(self._module, config_xml, warnings)
|
||||
|
||||
commit = not self._module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(self._module)
|
||||
else:
|
||||
discard_changes(self._module)
|
||||
result['changed'] = True
|
||||
|
||||
if self._module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
result['commands'] = config_xmls
|
||||
|
||||
changed_lag_interfaces_facts = self.get_lag_interfaces_facts()
|
||||
|
||||
result['before'] = existing_lag_interfaces_facts
|
||||
if result['changed']:
|
||||
result['after'] = changed_lag_interfaces_facts
|
||||
|
||||
return result
|
||||
|
||||
def set_config(self, existing_lag_interfaces_facts):
|
||||
""" Collect the configuration from the args passed to the module,
|
||||
collect the current configuration (as a dict from facts)
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
want = self._module.params['config']
|
||||
have = existing_lag_interfaces_facts
|
||||
resp = self.set_state(want, have)
|
||||
return to_list(resp)
|
||||
|
||||
def set_state(self, want, have):
|
||||
""" Select the appropriate function based on the state provided
|
||||
:param want: the desired configuration as a dictionary
|
||||
:param have: the current configuration as a dictionary
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
root = build_root_xml_node('interfaces')
|
||||
state = self._module.params['state']
|
||||
if state == 'overridden':
|
||||
config_xmls = self._state_overridden(want, have)
|
||||
elif state == 'deleted':
|
||||
config_xmls = self._state_deleted(want, have)
|
||||
elif state == 'merged':
|
||||
config_xmls = self._state_merged(want, have)
|
||||
elif state == 'replaced':
|
||||
config_xmls = self._state_replaced(want, have)
|
||||
|
||||
for xml in config_xmls:
|
||||
root.append(xml)
|
||||
|
||||
return tostring(root)
|
||||
|
||||
def _state_replaced(self, want, have):
|
||||
""" The xml configuration generator when state is replaced
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
intf_xml.extend(self._state_deleted(want, have))
|
||||
intf_xml.extend(self._state_merged(want, have))
|
||||
|
||||
return intf_xml
|
||||
|
||||
def _state_overridden(self, want, have):
|
||||
""" The xml configuration generator when state is overridden
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
interface_xmls_obj = []
|
||||
# replace interface config with data in want
|
||||
interface_xmls_obj.extend(self._state_replaced(want, have))
|
||||
|
||||
# delete interface config if interface in have not present in want
|
||||
delete_obj = []
|
||||
for have_obj in have:
|
||||
for want_obj in want:
|
||||
if have_obj['name'] == want_obj['name']:
|
||||
break
|
||||
else:
|
||||
delete_obj.append(have_obj)
|
||||
|
||||
if delete_obj:
|
||||
interface_xmls_obj.extend(self._state_deleted(delete_obj, have))
|
||||
return interface_xmls_obj
|
||||
|
||||
def _state_merged(self, want, have):
|
||||
""" The xml configuration generator when state is merged
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to merge the provided into
|
||||
the current configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
config_filter = """
|
||||
<configuration>
|
||||
<interfaces/>
|
||||
</configuration>
|
||||
"""
|
||||
data = get_resource_config(self._connection, config_filter=config_filter)
|
||||
|
||||
for config in want:
|
||||
lag_name = config['name']
|
||||
|
||||
# if lag interface not already configured fail module.
|
||||
if not data.xpath("configuration/interfaces/interface[name='%s']" % lag_name):
|
||||
self._module.fail_json(msg="lag interface %s not configured, configure interface"
|
||||
" %s before assigning members to lag" % (lag_name, lag_name))
|
||||
|
||||
lag_intf_root = build_root_xml_node('interface')
|
||||
build_child_xml_node(lag_intf_root, 'name', lag_name)
|
||||
ether_options_node = build_subtree(lag_intf_root, 'aggregated-ether-options')
|
||||
if config['mode']:
|
||||
|
||||
lacp_node = build_child_xml_node(ether_options_node, 'lacp')
|
||||
build_child_xml_node(lacp_node, config['mode'])
|
||||
|
||||
link_protection = config['link_protection']
|
||||
if link_protection:
|
||||
build_child_xml_node(ether_options_node, 'link-protection')
|
||||
elif link_protection is False:
|
||||
build_child_xml_node(ether_options_node, 'link-protection', None, {'delete': 'delete'})
|
||||
|
||||
intf_xml.append(lag_intf_root)
|
||||
|
||||
members = config['members']
|
||||
for member in members:
|
||||
lag_member_intf_root = build_root_xml_node('interface')
|
||||
build_child_xml_node(lag_member_intf_root, 'name', member['member'])
|
||||
lag_node = build_subtree(lag_member_intf_root, 'ether-options/ieee-802.3ad')
|
||||
build_child_xml_node(lag_node, 'bundle', config['name'])
|
||||
|
||||
link_type = member.get('link_type')
|
||||
if link_type == "primary":
|
||||
build_child_xml_node(lag_node, 'primary')
|
||||
elif link_type == "backup":
|
||||
build_child_xml_node(lag_node, 'backup')
|
||||
|
||||
intf_xml.append(lag_member_intf_root)
|
||||
|
||||
return intf_xml
|
||||
|
||||
def _state_deleted(self, want, have):
|
||||
""" The xml configuration generator when state is deleted
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to remove the current configuration
|
||||
of the provided objects
|
||||
"""
|
||||
intf_xml = []
|
||||
intf_obj = want
|
||||
|
||||
if not intf_obj:
|
||||
# delete lag interfaces attribute for all the interface
|
||||
intf_obj = have
|
||||
|
||||
for config in intf_obj:
|
||||
lag_name = config['name']
|
||||
lag_intf_root = build_root_xml_node('interface')
|
||||
build_child_xml_node(lag_intf_root, 'name', lag_name)
|
||||
|
||||
lag_node = build_subtree(lag_intf_root, 'aggregated-ether-options')
|
||||
build_child_xml_node(lag_node, 'link-protection', None, {'delete': 'delete'})
|
||||
|
||||
lacp_node = build_child_xml_node(lag_node, 'lacp')
|
||||
build_child_xml_node(lacp_node, 'active', None, {'delete': 'delete'})
|
||||
build_child_xml_node(lacp_node, 'passive', None, {'delete': 'delete'})
|
||||
|
||||
intf_xml.append(lag_intf_root)
|
||||
|
||||
# delete lag configuration from member interfaces
|
||||
for interface_obj in have:
|
||||
if lag_name == interface_obj['name']:
|
||||
for member in interface_obj.get('members', []):
|
||||
lag_member_intf_root = build_root_xml_node('interface')
|
||||
build_child_xml_node(lag_member_intf_root, 'name', member['member'])
|
||||
lag_node = build_subtree(lag_member_intf_root, 'ether-options/ieee-802.3ad')
|
||||
build_child_xml_node(lag_node, 'bundle', None, {'delete': 'delete'})
|
||||
build_child_xml_node(lag_node, 'primary', None, {'delete': 'delete'})
|
||||
build_child_xml_node(lag_node, 'backup', None, {'delete': 'delete'})
|
||||
intf_xml.append(lag_member_intf_root)
|
||||
|
||||
return intf_xml
|
@ -1,177 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos_lldp class
|
||||
It is in this file where the current configuration (as dict)
|
||||
is compared to the provided configuration (as dict) and the command set
|
||||
necessary to bring the current configuration to it's desired end-state is
|
||||
created
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.junos.junos import locked_config, load_config, commit_configuration, discard_changes, tostring
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.module_utils.network.junos.facts.facts import Facts
|
||||
from ansible.module_utils.network.common.netconf import build_root_xml_node, build_child_xml_node
|
||||
|
||||
|
||||
class Lldp_global(ConfigBase):
|
||||
"""
|
||||
The junos_lldp class
|
||||
"""
|
||||
|
||||
gather_subset = [
|
||||
'!all',
|
||||
'!min',
|
||||
]
|
||||
|
||||
gather_network_resources = [
|
||||
'lldp_global',
|
||||
]
|
||||
|
||||
def __init__(self, module):
|
||||
super(Lldp_global, self).__init__(module)
|
||||
|
||||
def get_lldp_global_facts(self):
|
||||
""" Get the 'facts' (the current configuration)
|
||||
:rtype: A dictionary
|
||||
:returns: The current configuration as a dictionary
|
||||
"""
|
||||
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
|
||||
lldp_facts = facts['ansible_network_resources'].get('lldp_global')
|
||||
if not lldp_facts:
|
||||
return {}
|
||||
return lldp_facts
|
||||
|
||||
def execute_module(self):
|
||||
""" Execute the module
|
||||
:rtype: A dictionary
|
||||
:returns: The result from module execution
|
||||
"""
|
||||
result = {'changed': False}
|
||||
|
||||
existing_lldp_global_facts = self.get_lldp_global_facts()
|
||||
config_xmls = self.set_config(existing_lldp_global_facts)
|
||||
|
||||
with locked_config(self._module):
|
||||
for config_xml in to_list(config_xmls):
|
||||
diff = load_config(self._module, config_xml, [])
|
||||
|
||||
commit = not self._module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(self._module)
|
||||
else:
|
||||
discard_changes(self._module)
|
||||
result['changed'] = True
|
||||
|
||||
if self._module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
result['commands'] = config_xmls
|
||||
|
||||
changed_lldp_global_facts = self.get_lldp_global_facts()
|
||||
|
||||
result['before'] = existing_lldp_global_facts
|
||||
if result['changed']:
|
||||
result['after'] = changed_lldp_global_facts
|
||||
|
||||
return result
|
||||
|
||||
def set_config(self, existing_lldp_global_facts):
|
||||
""" Collect the configuration from the args passed to the module,
|
||||
collect the current configuration (as a dict from facts)
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
want = self._module.params['config']
|
||||
have = existing_lldp_global_facts
|
||||
resp = self.set_state(want, have)
|
||||
return to_list(resp)
|
||||
|
||||
def set_state(self, want, have):
|
||||
""" Select the appropriate function based on the state provided
|
||||
:param want: the desired configuration as a dictionary
|
||||
:param have: the current configuration as a dictionary
|
||||
:rtype: A list
|
||||
:returns: the list xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
root = build_root_xml_node('protocols')
|
||||
state = self._module.params['state']
|
||||
if state == 'deleted':
|
||||
config_xmls = self._state_deleted(want, have)
|
||||
elif state == 'merged':
|
||||
config_xmls = self._state_merged(want, have)
|
||||
elif state == 'replaced':
|
||||
config_xmls = self._state_replaced(want, have)
|
||||
|
||||
for xml in config_xmls:
|
||||
root.append(xml)
|
||||
return tostring(root)
|
||||
|
||||
def _state_replaced(self, want, have):
|
||||
""" The xml configuration generator when state is merged
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to merge the provided into
|
||||
the current configuration
|
||||
"""
|
||||
lldp_xml = []
|
||||
lldp_xml.extend(self._state_deleted(want, have))
|
||||
lldp_xml.extend(self._state_merged(want, have))
|
||||
|
||||
return lldp_xml
|
||||
|
||||
def _state_merged(self, want, have):
|
||||
""" Select the appropriate function based on the state provided
|
||||
:param want: the desired configuration as a dictionary
|
||||
:param have: the current configuration as a dictionary
|
||||
:rtype: A list
|
||||
:returns: the list xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
lldp_xml = []
|
||||
|
||||
lldp_root = build_root_xml_node('lldp')
|
||||
if want.get('address'):
|
||||
build_child_xml_node(lldp_root, 'management-address', want['address'])
|
||||
if want.get('interval'):
|
||||
build_child_xml_node(lldp_root, 'advertisement-interval', want['interval'])
|
||||
if want.get('transmit_delay'):
|
||||
build_child_xml_node(lldp_root, 'transmit-delay', want['transmit_delay'])
|
||||
if want.get('hold_multiplier'):
|
||||
build_child_xml_node(lldp_root, 'hold-multiplier', want['hold_multiplier'])
|
||||
enable = want.get('enable')
|
||||
if enable is not None:
|
||||
if enable is False:
|
||||
build_child_xml_node(lldp_root, 'disable')
|
||||
else:
|
||||
build_child_xml_node(lldp_root, 'disable', None, {'delete': 'delete'})
|
||||
else:
|
||||
build_child_xml_node(lldp_root, 'disable', None, {'delete': 'delete'})
|
||||
lldp_xml.append(lldp_root)
|
||||
|
||||
return lldp_xml
|
||||
|
||||
def _state_deleted(self, want, have):
|
||||
""" The command generator when state is deleted
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to remove the current configuration
|
||||
of the provided objects
|
||||
"""
|
||||
lldp_xml = []
|
||||
|
||||
lldp_root = build_root_xml_node('lldp')
|
||||
build_child_xml_node(lldp_root, 'management-address', None, {'delete': 'delete'})
|
||||
build_child_xml_node(lldp_root, 'advertisement-interval', None, {'delete': 'delete'})
|
||||
build_child_xml_node(lldp_root, 'transmit-delay', None, {'delete': 'delete'})
|
||||
build_child_xml_node(lldp_root, 'hold-multiplier', None, {'delete': 'delete'})
|
||||
build_child_xml_node(lldp_root, 'disable', None, {'delete': 'delete'})
|
||||
lldp_xml.append(lldp_root)
|
||||
return lldp_xml
|
@ -1,205 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos_lldp_interfaces class
|
||||
It is in this file where the current configuration (as dict)
|
||||
is compared to the provided configuration (as dict) and the command set
|
||||
necessary to bring the current configuration to it's desired end-state is
|
||||
created
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.module_utils.network.junos.facts.facts import Facts
|
||||
from ansible.module_utils.network.junos.junos import locked_config, load_config, commit_configuration, discard_changes, tostring
|
||||
from ansible.module_utils.network.common.netconf import build_root_xml_node, build_child_xml_node, build_subtree
|
||||
|
||||
|
||||
class Lldp_interfaces(ConfigBase):
|
||||
"""
|
||||
The junos_lldp_interfaces class
|
||||
"""
|
||||
|
||||
gather_subset = [
|
||||
'!all',
|
||||
'!min',
|
||||
]
|
||||
|
||||
gather_network_resources = [
|
||||
'lldp_interfaces',
|
||||
]
|
||||
|
||||
def __init__(self, module):
|
||||
super(Lldp_interfaces, self).__init__(module)
|
||||
|
||||
def get_lldp_interfaces_facts(self):
|
||||
""" Get the 'facts' (the current configuration)
|
||||
:rtype: A dictionary
|
||||
:returns: The current configuration as a dictionary
|
||||
"""
|
||||
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
|
||||
lldp_interfaces_facts = facts['ansible_network_resources'].get('lldp_interfaces')
|
||||
if not lldp_interfaces_facts:
|
||||
return []
|
||||
return lldp_interfaces_facts
|
||||
|
||||
def execute_module(self):
|
||||
""" Execute the module
|
||||
:rtype: A dictionary
|
||||
:returns: The result from module execution
|
||||
"""
|
||||
result = {'changed': False}
|
||||
warnings = list()
|
||||
|
||||
existing_lldp_interfaces_facts = self.get_lldp_interfaces_facts()
|
||||
config_xmls = self.set_config(existing_lldp_interfaces_facts)
|
||||
|
||||
with locked_config(self._module):
|
||||
for config_xml in to_list(config_xmls):
|
||||
diff = load_config(self._module, config_xml, warnings)
|
||||
|
||||
commit = not self._module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(self._module)
|
||||
else:
|
||||
discard_changes(self._module)
|
||||
result['changed'] = True
|
||||
|
||||
if self._module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
result['commands'] = config_xmls
|
||||
|
||||
changed_lldp_interfaces_facts = self.get_lldp_interfaces_facts()
|
||||
|
||||
result['before'] = existing_lldp_interfaces_facts
|
||||
if result['changed']:
|
||||
result['after'] = changed_lldp_interfaces_facts
|
||||
|
||||
return result
|
||||
|
||||
def set_config(self, existing_lldp_interfaces_facts):
|
||||
""" Collect the configuration from the args passed to the module,
|
||||
collect the current configuration (as a dict from facts)
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
want = self._module.params['config']
|
||||
have = existing_lldp_interfaces_facts
|
||||
resp = self.set_state(want, have)
|
||||
return to_list(resp)
|
||||
|
||||
def set_state(self, want, have):
|
||||
""" Select the appropriate function based on the state provided
|
||||
:param want: the desired configuration as a dictionary
|
||||
:param have: the current configuration as a dictionary
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
root = build_root_xml_node('protocols')
|
||||
lldp_intf_ele = build_subtree(root, 'lldp')
|
||||
|
||||
state = self._module.params['state']
|
||||
if state == 'overridden':
|
||||
config_xmls = self._state_overridden(want, have)
|
||||
elif state == 'deleted':
|
||||
config_xmls = self._state_deleted(want, have)
|
||||
elif state == 'merged':
|
||||
config_xmls = self._state_merged(want, have)
|
||||
elif state == 'replaced':
|
||||
config_xmls = self._state_replaced(want, have)
|
||||
|
||||
for xml in config_xmls:
|
||||
lldp_intf_ele.append(xml)
|
||||
|
||||
return tostring(root)
|
||||
|
||||
def _state_replaced(self, want, have):
|
||||
""" The xml configuration generator when state is replaced
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
lldp_intf_xml = []
|
||||
lldp_intf_xml.extend(self._state_deleted(want, have))
|
||||
lldp_intf_xml.extend(self._state_merged(want, have))
|
||||
|
||||
return lldp_intf_xml
|
||||
|
||||
def _state_overridden(self, want, have):
|
||||
""" The xml configuration generator when state is overridden
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
lldp_intf_xmls_obj = []
|
||||
|
||||
# replace interface config with data in want
|
||||
lldp_intf_xmls_obj.extend(self._state_replaced(want, have))
|
||||
|
||||
# delete interface config if interface in have not present in want
|
||||
delete_obj = []
|
||||
for have_obj in have:
|
||||
for want_obj in want:
|
||||
if have_obj['name'] == want_obj['name']:
|
||||
break
|
||||
else:
|
||||
delete_obj.append(have_obj)
|
||||
|
||||
if len(delete_obj):
|
||||
lldp_intf_xmls_obj.extend(self._state_deleted(delete_obj, have))
|
||||
|
||||
return lldp_intf_xmls_obj
|
||||
|
||||
def _state_merged(self, want, have):
|
||||
""" The xml configuration generator when state is merged
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to merge the provided into
|
||||
the current configuration
|
||||
"""
|
||||
lldp_intf_xml = []
|
||||
for config in want:
|
||||
lldp_intf_root = build_root_xml_node('interface')
|
||||
|
||||
if config.get('name'):
|
||||
build_child_xml_node(lldp_intf_root, 'name', config['name'])
|
||||
|
||||
if config.get('enabled') is not None:
|
||||
if config['enabled'] is False:
|
||||
build_child_xml_node(lldp_intf_root, 'disable')
|
||||
else:
|
||||
build_child_xml_node(lldp_intf_root, 'disable', None, {'delete': 'delete'})
|
||||
else:
|
||||
build_child_xml_node(lldp_intf_root, 'disable', None, {'delete': 'delete'})
|
||||
lldp_intf_xml.append(lldp_intf_root)
|
||||
return lldp_intf_xml
|
||||
|
||||
def _state_deleted(self, want, have):
|
||||
""" The xml configuration generator when state is deleted
|
||||
:rtype: A list
|
||||
:returns: the xml configuration necessary to remove the current configuration
|
||||
of the provided objects
|
||||
"""
|
||||
lldp_intf_xml = []
|
||||
intf_obj = want
|
||||
|
||||
if not intf_obj:
|
||||
# delete lldp interfaces attribute from all the existing interface
|
||||
intf_obj = have
|
||||
|
||||
for config in intf_obj:
|
||||
lldp_intf_root = build_root_xml_node('interface')
|
||||
lldp_intf_root.attrib.update({'delete': 'delete'})
|
||||
build_child_xml_node(lldp_intf_root, 'name', config['name'])
|
||||
|
||||
lldp_intf_xml.append(lldp_intf_root)
|
||||
|
||||
return lldp_intf_xml
|
@ -1,235 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos_static_routes class
|
||||
It is in this file where the current configuration (as dict)
|
||||
is compared to the provided configuration (as dict) and the command set
|
||||
necessary to bring the current configuration to it's desired end-state is
|
||||
created
|
||||
"""
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.module_utils.network.junos.facts.facts import Facts
|
||||
from ansible.module_utils.network.junos.junos import (locked_config,
|
||||
load_config,
|
||||
commit_configuration,
|
||||
discard_changes,
|
||||
tostring)
|
||||
from ansible.module_utils.network.common.netconf import (build_root_xml_node,
|
||||
build_child_xml_node)
|
||||
|
||||
|
||||
class Static_routes(ConfigBase):
|
||||
"""
|
||||
The junos_static_routes class
|
||||
"""
|
||||
|
||||
gather_subset = [
|
||||
'!all',
|
||||
'!min',
|
||||
]
|
||||
|
||||
gather_network_resources = [
|
||||
'static_routes',
|
||||
]
|
||||
|
||||
def __init__(self, module):
|
||||
super(Static_routes, self).__init__(module)
|
||||
|
||||
def get_static_routes_facts(self):
|
||||
""" Get the 'facts' (the current configuration)
|
||||
|
||||
:rtype: A dictionary
|
||||
:returns: The current configuration as a dictionary
|
||||
"""
|
||||
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
|
||||
static_routes_facts = facts['ansible_network_resources'].get('static_routes')
|
||||
if not static_routes_facts:
|
||||
return []
|
||||
return static_routes_facts
|
||||
|
||||
def execute_module(self):
|
||||
""" Execute the module
|
||||
|
||||
:rtype: A dictionary
|
||||
:returns: The result from module execution
|
||||
"""
|
||||
result = {'changed': False}
|
||||
warnings = list()
|
||||
|
||||
existing_static_routes_facts = self.get_static_routes_facts()
|
||||
config_xmls = self.set_config(existing_static_routes_facts)
|
||||
|
||||
with locked_config(self._module):
|
||||
for config_xml in to_list(config_xmls):
|
||||
diff = load_config(self._module, config_xml, [])
|
||||
|
||||
commit = not self._module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(self._module)
|
||||
else:
|
||||
discard_changes(self._module)
|
||||
result['changed'] = True
|
||||
|
||||
if self._module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
result['xml'] = config_xmls
|
||||
changed_static_routes_facts = self.get_static_routes_facts()
|
||||
|
||||
result['before'] = existing_static_routes_facts
|
||||
if result['changed']:
|
||||
result['after'] = changed_static_routes_facts
|
||||
|
||||
result['warnings'] = warnings
|
||||
return result
|
||||
|
||||
def set_config(self, existing_static_routes_facts):
|
||||
""" Collect the configuration from the args passed to the module,
|
||||
collect the current configuration (as a dict from facts)
|
||||
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
want = self._module.params['config']
|
||||
have = existing_static_routes_facts
|
||||
resp = self.set_state(want, have)
|
||||
return to_list(resp)
|
||||
|
||||
def set_state(self, want, have):
|
||||
""" Select the appropriate function based on the state provided
|
||||
|
||||
:param want: the desired configuration as a dictionary
|
||||
:param have: the current configuration as a dictionary
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
state = self._module.params['state']
|
||||
root = build_root_xml_node('configuration')
|
||||
routing_options = build_child_xml_node(root, 'routing-options')
|
||||
routing_instances = build_child_xml_node(root, 'routing-instances')
|
||||
if state == 'overridden':
|
||||
config_xmls = self._state_overridden(want, have)
|
||||
elif state == 'deleted':
|
||||
config_xmls = self._state_deleted(want, have)
|
||||
elif state == 'merged':
|
||||
config_xmls = self._state_merged(want, have)
|
||||
elif state == 'replaced':
|
||||
config_xmls = self._state_replaced(want, have)
|
||||
|
||||
for xml in config_xmls:
|
||||
if xml['root_type'] == 'routing-options':
|
||||
routing_options.append(xml['static_route_xml'])
|
||||
elif xml['root_type'] == 'routing-instances':
|
||||
routing_instances.append(xml['static_route_xml'])
|
||||
|
||||
return [tostring(xml) for xml in root.getchildren()]
|
||||
|
||||
def _state_replaced(self, want, have):
|
||||
""" The command generator when state is replaced
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
static_route_xml = []
|
||||
static_route_xml.extend(self._state_deleted(want, have))
|
||||
static_route_xml.extend(self._state_merged(want, have))
|
||||
return static_route_xml
|
||||
|
||||
def _state_overridden(self, want, have):
|
||||
""" The command generator when state is overridden
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
static_route_xml = []
|
||||
static_route_xml.extend(self._state_deleted(have, have))
|
||||
static_route_xml.extend(self._state_merged(want, have))
|
||||
return static_route_xml
|
||||
|
||||
def _state_deleted(self, want, have):
|
||||
""" The command generator when state is deleted
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
if not want:
|
||||
want = have
|
||||
static_route_xml = self._state_merged(want, have, delete={'delete': 'delete'})
|
||||
return static_route_xml
|
||||
|
||||
def _state_merged(self, want, have, delete=None):
|
||||
""" The command generator when state is merged
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
static_route_xml = []
|
||||
root_type = ""
|
||||
vrf_name = None
|
||||
for config in want:
|
||||
if config.get('vrf'):
|
||||
vrf_name = config['vrf']
|
||||
if vrf_name:
|
||||
root_type = 'routing-instances'
|
||||
instance = build_root_xml_node('instance')
|
||||
build_child_xml_node(instance, 'name', vrf_name)
|
||||
routing_options = build_child_xml_node(instance, 'routing-options')
|
||||
else:
|
||||
root_type = 'routing-options'
|
||||
|
||||
for afi in config['address_families']:
|
||||
protocol = afi['afi']
|
||||
if protocol == 'ipv6':
|
||||
if vrf_name:
|
||||
rib_route_root = build_child_xml_node(routing_options, 'rib')
|
||||
build_child_xml_node(rib_route_root, 'name', vrf_name + '.inet6.0')
|
||||
else:
|
||||
rib_route_root = build_root_xml_node('rib')
|
||||
build_child_xml_node(rib_route_root, 'name', 'inet6.0')
|
||||
static_route_root = build_child_xml_node(rib_route_root, 'static')
|
||||
elif protocol == 'ipv4':
|
||||
if vrf_name:
|
||||
static_route_root = build_child_xml_node(routing_options, 'static')
|
||||
else:
|
||||
static_route_root = build_root_xml_node('static')
|
||||
|
||||
if afi.get('routes'):
|
||||
for route in afi['routes']:
|
||||
route_node = build_child_xml_node(static_route_root, 'route')
|
||||
if delete:
|
||||
route_node.attrib.update(delete)
|
||||
if route.get('dest'):
|
||||
build_child_xml_node(route_node, 'name', route['dest'])
|
||||
if not delete:
|
||||
if route.get('metric'):
|
||||
build_child_xml_node(route_node, 'metric', route['metric'])
|
||||
if route.get('next_hop'):
|
||||
for hop in route['next_hop']:
|
||||
build_child_xml_node(route_node, 'next-hop', hop['forward_router_address'])
|
||||
elif delete:
|
||||
if vrf_name:
|
||||
instance.attrib.update(delete)
|
||||
static_route_root.attrib.update(delete)
|
||||
|
||||
if vrf_name:
|
||||
static_route_xml.append({'root_type': root_type, 'static_route_xml': instance})
|
||||
else:
|
||||
if protocol == 'ipv6':
|
||||
static_route_xml.append({'root_type': root_type, 'static_route_xml': rib_route_root})
|
||||
else:
|
||||
static_route_xml.append({'root_type': root_type, 'static_route_xml': static_route_root})
|
||||
return static_route_xml
|
@ -1,208 +0,0 @@
|
||||
# Copyright (C) 2019 Red Hat, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# 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.
|
||||
#
|
||||
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
The junos_vlans class
|
||||
It is in this file where the current configuration (as dict)
|
||||
is compared to the provided configuration (as dict) and the command set
|
||||
necessary to bring the current configuration to it's desired end-state is
|
||||
created
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
from ansible.module_utils.network.common.cfg.base import ConfigBase
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.module_utils.network.junos.facts.facts import Facts
|
||||
from ansible.module_utils.network.junos.junos import (locked_config,
|
||||
load_config,
|
||||
commit_configuration,
|
||||
discard_changes,
|
||||
tostring)
|
||||
from ansible.module_utils.network.common.netconf import (build_root_xml_node,
|
||||
build_child_xml_node)
|
||||
|
||||
|
||||
class Vlans(ConfigBase):
|
||||
"""
|
||||
The junos_vlans class
|
||||
"""
|
||||
|
||||
gather_subset = [
|
||||
'!all',
|
||||
'!min',
|
||||
]
|
||||
|
||||
gather_network_resources = [
|
||||
'vlans',
|
||||
]
|
||||
|
||||
def __init__(self, module):
|
||||
super(Vlans, self).__init__(module)
|
||||
|
||||
def get_vlans_facts(self):
|
||||
""" Get the 'facts' (the current configuration)
|
||||
|
||||
:rtype: A dictionary
|
||||
:returns: The current configuration as a dictionary
|
||||
"""
|
||||
facts, _warnings = Facts(self._module).get_facts(
|
||||
self.gather_subset, self.gather_network_resources)
|
||||
vlans_facts = facts['ansible_network_resources'].get('vlans')
|
||||
if not vlans_facts:
|
||||
return []
|
||||
return vlans_facts
|
||||
|
||||
def execute_module(self):
|
||||
""" Execute the module
|
||||
|
||||
:rtype: A dictionary
|
||||
:returns: The result from module execution
|
||||
"""
|
||||
result = {'changed': False}
|
||||
warnings = list()
|
||||
|
||||
existing_vlans_facts = self.get_vlans_facts()
|
||||
config_xmls = self.set_config(existing_vlans_facts)
|
||||
|
||||
with locked_config(self._module):
|
||||
for config_xml in to_list(config_xmls):
|
||||
diff = load_config(self._module, config_xml, warnings)
|
||||
|
||||
commit = not self._module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(self._module)
|
||||
else:
|
||||
discard_changes(self._module)
|
||||
result['changed'] = True
|
||||
|
||||
if self._module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
result['commands'] = config_xmls
|
||||
|
||||
changed_vlans_facts = self.get_vlans_facts()
|
||||
|
||||
result['before'] = existing_vlans_facts
|
||||
if result['changed']:
|
||||
result['after'] = changed_vlans_facts
|
||||
|
||||
result['warnings'] = warnings
|
||||
return result
|
||||
|
||||
def set_config(self, existing_vlans_facts):
|
||||
""" Collect the configuration from the args passed to the module,
|
||||
collect the current configuration (as a dict from facts)
|
||||
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
want = self._module.params['config']
|
||||
have = existing_vlans_facts
|
||||
resp = self.set_state(want, have)
|
||||
return to_list(resp)
|
||||
|
||||
def set_state(self, want, have):
|
||||
""" Select the appropriate function based on the state provided
|
||||
|
||||
:param want: the desired configuration as a dictionary
|
||||
:param have: the current configuration as a dictionary
|
||||
:rtype: A list
|
||||
:returns: the commands necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
root = build_root_xml_node('vlans')
|
||||
state = self._module.params['state']
|
||||
if state == 'overridden':
|
||||
config_xmls = self._state_overridden(want, have)
|
||||
elif state == 'deleted':
|
||||
config_xmls = self._state_deleted(want, have)
|
||||
elif state == 'merged':
|
||||
config_xmls = self._state_merged(want, have)
|
||||
elif state == 'replaced':
|
||||
config_xmls = self._state_replaced(want, have)
|
||||
|
||||
for xml in config_xmls:
|
||||
root.append(xml)
|
||||
|
||||
return tostring(root)
|
||||
|
||||
def _state_replaced(self, want, have):
|
||||
""" The command generator when state is replaced
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
intf_xml.extend(self._state_deleted(want, have))
|
||||
intf_xml.extend(self._state_merged(want, have))
|
||||
return intf_xml
|
||||
|
||||
def _state_overridden(self, want, have):
|
||||
""" The command generator when state is overridden
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml necessary to migrate the current configuration
|
||||
to the desired configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
intf_xml.extend(self._state_deleted(have, have))
|
||||
intf_xml.extend(self._state_merged(want, have))
|
||||
return intf_xml
|
||||
|
||||
def _state_merged(self, want, have):
|
||||
""" The command generator when state is merged
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml necessary to merge the provided into
|
||||
the current configuration
|
||||
"""
|
||||
intf_xml = []
|
||||
|
||||
for config in want:
|
||||
vlan_name = str(config['name'])
|
||||
vlan_id = str(config['vlan_id'])
|
||||
vlan_description = config.get('description')
|
||||
vlan_root = build_root_xml_node('vlan')
|
||||
build_child_xml_node(vlan_root, 'name', vlan_name)
|
||||
build_child_xml_node(vlan_root, 'vlan-id', vlan_id)
|
||||
if vlan_description:
|
||||
build_child_xml_node(vlan_root, 'description',
|
||||
vlan_description)
|
||||
intf_xml.append(vlan_root)
|
||||
return intf_xml
|
||||
|
||||
def _state_deleted(self, want, have):
|
||||
""" The command generator when state is deleted
|
||||
|
||||
:rtype: A list
|
||||
:returns: the xml necessary to remove the current configuration
|
||||
of the provided objects
|
||||
"""
|
||||
intf_xml = []
|
||||
|
||||
if not want:
|
||||
want = have
|
||||
|
||||
for config in want:
|
||||
vlan_name = config['name']
|
||||
vlan_root = build_root_xml_node('vlan')
|
||||
vlan_root.attrib.update({'delete': 'delete'})
|
||||
build_child_xml_node(vlan_root, 'name', vlan_name)
|
||||
intf_xml.append(vlan_root)
|
||||
return intf_xml
|
@ -1,83 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The facts class for junos
|
||||
this file validates each subset of facts and selectively
|
||||
calls the appropriate facts gathering function
|
||||
"""
|
||||
|
||||
from ansible.module_utils.network.common.facts.facts import FactsBase
|
||||
from ansible.module_utils.network.junos.junos import HAS_PYEZ
|
||||
from ansible.module_utils.network.junos.facts.legacy.base import Default, Hardware, Config, Interfaces, OFacts
|
||||
from ansible.module_utils.network.junos.facts.interfaces.interfaces import InterfacesFacts
|
||||
from ansible.module_utils.network.junos.facts.lacp.lacp import LacpFacts
|
||||
from ansible.module_utils.network.junos.facts.lacp_interfaces.lacp_interfaces import Lacp_interfacesFacts
|
||||
from ansible.module_utils.network.junos.facts.lag_interfaces.lag_interfaces import Lag_interfacesFacts
|
||||
from ansible.module_utils.network.junos.facts.l3_interfaces.l3_interfaces import L3_interfacesFacts
|
||||
from ansible.module_utils.network.junos.facts.lldp_global.lldp_global import Lldp_globalFacts
|
||||
from ansible.module_utils.network.junos.facts.lldp_interfaces.lldp_interfaces import Lldp_interfacesFacts
|
||||
from ansible.module_utils.network.junos.facts.vlans.vlans import VlansFacts
|
||||
from ansible.module_utils.network.junos.facts.l2_interfaces.l2_interfaces import L2_interfacesFacts
|
||||
from ansible.module_utils.network.junos.facts.static_routes.static_routes import Static_routesFacts
|
||||
|
||||
FACT_LEGACY_SUBSETS = dict(
|
||||
default=Default,
|
||||
hardware=Hardware,
|
||||
config=Config,
|
||||
interfaces=Interfaces,
|
||||
)
|
||||
FACT_RESOURCE_SUBSETS = dict(
|
||||
interfaces=InterfacesFacts,
|
||||
lacp=LacpFacts,
|
||||
lacp_interfaces=Lacp_interfacesFacts,
|
||||
lag_interfaces=Lag_interfacesFacts,
|
||||
l2_interfaces=L2_interfacesFacts,
|
||||
l3_interfaces=L3_interfacesFacts,
|
||||
lldp_global=Lldp_globalFacts,
|
||||
lldp_interfaces=Lldp_interfacesFacts,
|
||||
vlans=VlansFacts,
|
||||
static_routes=Static_routesFacts
|
||||
)
|
||||
|
||||
|
||||
class Facts(FactsBase):
|
||||
""" The fact class for junos
|
||||
"""
|
||||
|
||||
VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys())
|
||||
VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys())
|
||||
|
||||
def __init__(self, module):
|
||||
super(Facts, self).__init__(module)
|
||||
|
||||
def get_facts(self, legacy_facts_type=None, resource_facts_type=None, data=None):
|
||||
""" Collect the facts for junos
|
||||
:param legacy_facts_type: List of legacy facts types
|
||||
:param resource_facts_type: List of resource fact types
|
||||
:param data: previously collected conf
|
||||
:rtype: dict
|
||||
:return: the facts gathered
|
||||
"""
|
||||
if self.VALID_RESOURCE_SUBSETS:
|
||||
self.get_network_resources_facts(FACT_RESOURCE_SUBSETS, resource_facts_type, data)
|
||||
|
||||
if not legacy_facts_type:
|
||||
legacy_facts_type = self._gather_subset
|
||||
# fetch old style facts only when explicitly mentioned in gather_subset option
|
||||
if 'ofacts' in legacy_facts_type:
|
||||
if HAS_PYEZ:
|
||||
self.ansible_facts.update(OFacts(self._module).populate())
|
||||
else:
|
||||
self._warnings.extend([
|
||||
'junos-eznc is required to gather old style facts but does not appear to be installed. '
|
||||
'It can be installed using `pip install junos-eznc`'])
|
||||
self.ansible_facts['ansible_net_gather_subset'].append('ofacts')
|
||||
legacy_facts_type.remove('ofacts')
|
||||
|
||||
if self.VALID_LEGACY_GATHER_SUBSETS:
|
||||
self.get_network_legacy_facts(FACT_LEGACY_SUBSETS, legacy_facts_type)
|
||||
|
||||
return self.ansible_facts, self._warnings
|
@ -1,111 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos interfaces fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
for a given resource, parsed, and the facts tree is populated
|
||||
based on the configuration.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.network.common import utils
|
||||
from ansible.module_utils.network.junos.argspec.interfaces.interfaces import InterfacesArgs
|
||||
from ansible.module_utils.network.junos.utils.utils import get_resource_config
|
||||
from ansible.module_utils.six import string_types
|
||||
try:
|
||||
from lxml import etree
|
||||
HAS_LXML = True
|
||||
except ImportError:
|
||||
HAS_LXML = False
|
||||
|
||||
|
||||
class InterfacesFacts(object):
|
||||
""" The junos interfaces fact class
|
||||
"""
|
||||
def __init__(self, module, subspec='config', options='options'):
|
||||
self._module = module
|
||||
self.argument_spec = InterfacesArgs.argument_spec
|
||||
spec = deepcopy(self.argument_spec)
|
||||
if subspec:
|
||||
if options:
|
||||
facts_argument_spec = spec[subspec][options]
|
||||
else:
|
||||
facts_argument_spec = spec[subspec]
|
||||
else:
|
||||
facts_argument_spec = spec
|
||||
|
||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
||||
|
||||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for interfaces
|
||||
|
||||
:param connection: the device connection
|
||||
:param data: previously collected configuration as lxml ElementTree root instance
|
||||
or valid xml sting
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
if not HAS_LXML:
|
||||
self._module.fail_json(msg='lxml is not installed.')
|
||||
|
||||
if not data:
|
||||
config_filter = """
|
||||
<configuration>
|
||||
<interfaces/>
|
||||
</configuration>
|
||||
"""
|
||||
data = get_resource_config(connection, config_filter=config_filter)
|
||||
|
||||
if isinstance(data, string_types):
|
||||
data = etree.fromstring(to_bytes(data, errors='surrogate_then_replace'))
|
||||
|
||||
resources = data.xpath('configuration/interfaces/interface')
|
||||
|
||||
objs = []
|
||||
for resource in resources:
|
||||
if resource is not None:
|
||||
obj = self.render_config(self.generated_spec, resource)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
facts = {}
|
||||
if objs:
|
||||
facts['interfaces'] = []
|
||||
params = utils.validate_config(self.argument_spec, {'config': objs})
|
||||
for cfg in params['config']:
|
||||
facts['interfaces'].append(utils.remove_empties(cfg))
|
||||
ansible_facts['ansible_network_resources'].update(facts)
|
||||
|
||||
return ansible_facts
|
||||
|
||||
def render_config(self, spec, conf):
|
||||
"""
|
||||
Render config as dictionary structure and delete keys
|
||||
from spec for null values
|
||||
|
||||
:param spec: The facts tree, generated from the argspec
|
||||
:param conf: The ElementTree instance of configuration object
|
||||
:rtype: dictionary
|
||||
:returns: The generated config
|
||||
"""
|
||||
config = deepcopy(spec)
|
||||
config['name'] = utils.get_xml_conf_arg(conf, 'name')
|
||||
config['description'] = utils.get_xml_conf_arg(conf, 'description')
|
||||
mtu = utils.get_xml_conf_arg(conf, 'mtu')
|
||||
config['mtu'] = int(mtu) if mtu else None
|
||||
config['speed'] = utils.get_xml_conf_arg(conf, 'speed')
|
||||
config['duplex'] = utils.get_xml_conf_arg(conf, 'link-mode')
|
||||
config['hold_time']['down'] = utils.get_xml_conf_arg(conf, 'hold-time/down')
|
||||
config['hold_time']['up'] = utils.get_xml_conf_arg(conf, 'hold-time/up')
|
||||
disable = utils.get_xml_conf_arg(conf, 'disable', data='tag')
|
||||
if disable:
|
||||
config['enabled'] = False
|
||||
else:
|
||||
config['enabled'] = True
|
||||
return utils.remove_empties(config)
|
@ -1,124 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos l2_interfaces fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
for a given resource, parsed, and the facts tree is populated
|
||||
based on the configuration.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.network.common import utils
|
||||
from ansible.module_utils.network.junos.argspec.l2_interfaces.l2_interfaces import L2_interfacesArgs
|
||||
from ansible.module_utils.network.junos.utils.utils import get_resource_config
|
||||
from ansible.module_utils.six import string_types
|
||||
try:
|
||||
from lxml import etree
|
||||
HAS_LXML = True
|
||||
except ImportError:
|
||||
HAS_LXML = False
|
||||
|
||||
|
||||
class L2_interfacesFacts(object):
|
||||
""" The junos l2_interfaces fact class
|
||||
"""
|
||||
|
||||
def __init__(self, module, subspec='config', options='options'):
|
||||
self._module = module
|
||||
self.argument_spec = L2_interfacesArgs.argument_spec
|
||||
spec = deepcopy(self.argument_spec)
|
||||
if subspec:
|
||||
if options:
|
||||
facts_argument_spec = spec[subspec][options]
|
||||
else:
|
||||
facts_argument_spec = spec[subspec]
|
||||
else:
|
||||
facts_argument_spec = spec
|
||||
|
||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
||||
|
||||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for interfaces
|
||||
:param connection: the device connection
|
||||
:param data: previously collected configuration as lxml ElementTree root instance
|
||||
or valid xml sting
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
if not HAS_LXML:
|
||||
self._module.fail_json(msg='lxml is not installed.')
|
||||
|
||||
if not data:
|
||||
config_filter = """
|
||||
<configuration>
|
||||
<interfaces/>
|
||||
</configuration>
|
||||
"""
|
||||
data = get_resource_config(connection, config_filter=config_filter)
|
||||
|
||||
if isinstance(data, string_types):
|
||||
data = etree.fromstring(to_bytes(data, errors='surrogate_then_replace'))
|
||||
|
||||
self._resources = data.xpath('configuration/interfaces/interface')
|
||||
|
||||
objs = []
|
||||
for resource in self._resources:
|
||||
if resource is not None:
|
||||
obj = self.render_config(self.generated_spec, resource)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
facts = {}
|
||||
if objs:
|
||||
facts['l2_interfaces'] = []
|
||||
params = utils.validate_config(self.argument_spec, {'config': objs})
|
||||
for cfg in params['config']:
|
||||
facts['l2_interfaces'].append(utils.remove_empties(cfg))
|
||||
ansible_facts['ansible_network_resources'].update(facts)
|
||||
return ansible_facts
|
||||
|
||||
def render_config(self, spec, conf):
|
||||
"""`
|
||||
Render config as dictionary structure and delete keys
|
||||
from spec for null values
|
||||
:param spec: The facts tree, generated from the argspec
|
||||
:param conf: The ElementTree instance of configuration object
|
||||
:rtype: dictionary
|
||||
:returns: The generated config
|
||||
"""
|
||||
config = deepcopy(spec)
|
||||
|
||||
enhanced_layer = True
|
||||
mode = utils.get_xml_conf_arg(conf, 'unit/family/ethernet-switching/interface-mode')
|
||||
|
||||
if mode is None:
|
||||
mode = utils.get_xml_conf_arg(conf, 'unit/family/ethernet-switching/port-mode')
|
||||
enhanced_layer = False
|
||||
|
||||
# Layer 2 is configured on interface
|
||||
if mode:
|
||||
config['name'] = utils.get_xml_conf_arg(conf, 'name')
|
||||
unit = utils.get_xml_conf_arg(conf, 'unit/name')
|
||||
config['unit'] = unit if unit else 0
|
||||
config['enhanced_layer'] = enhanced_layer
|
||||
|
||||
if mode == 'access':
|
||||
config['access'] = {}
|
||||
config['access']['vlan'] = utils.get_xml_conf_arg(conf, "unit/family/ethernet-switching/vlan/members")
|
||||
elif mode == 'trunk':
|
||||
config['trunk'] = {}
|
||||
vlan_members = conf.xpath('unit/family/ethernet-switching/vlan/members')
|
||||
if vlan_members:
|
||||
config['trunk']['allowed_vlans'] = []
|
||||
for vlan_member in vlan_members:
|
||||
config['trunk']['allowed_vlans'].append(vlan_member.text)
|
||||
|
||||
config['trunk']['native_vlan'] = utils.get_xml_conf_arg(conf, "native-vlan-id")
|
||||
|
||||
return utils.remove_empties(config)
|
@ -1,134 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos l3_interfaces fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
for a given resource, parsed, and the facts tree is populated
|
||||
based on the configuration.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.network.common import utils
|
||||
from ansible.module_utils.network.junos.argspec.l3_interfaces.l3_interfaces import L3_interfacesArgs
|
||||
from ansible.module_utils.six import string_types
|
||||
try:
|
||||
from lxml import etree
|
||||
HAS_LXML = True
|
||||
except ImportError:
|
||||
HAS_LXML = False
|
||||
|
||||
|
||||
class L3_interfacesFacts(object):
|
||||
""" The junos l3_interfaces fact class
|
||||
"""
|
||||
|
||||
def __init__(self, module, subspec='config', options='options'):
|
||||
self._module = module
|
||||
self.argument_spec = L3_interfacesArgs.argument_spec
|
||||
spec = deepcopy(self.argument_spec)
|
||||
if subspec:
|
||||
if options:
|
||||
facts_argument_spec = spec[subspec][options]
|
||||
else:
|
||||
facts_argument_spec = spec[subspec]
|
||||
else:
|
||||
facts_argument_spec = spec
|
||||
|
||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
||||
|
||||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for l3_interfaces
|
||||
:param connection: the device connection
|
||||
:param ansible_facts: Facts dictionary
|
||||
:param data: previously collected conf
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
if not HAS_LXML:
|
||||
self._module.fail_json(msg='lxml is not installed.')
|
||||
|
||||
if not data:
|
||||
config_filter = """
|
||||
<configuration>
|
||||
<interfaces/>
|
||||
</configuration>
|
||||
"""
|
||||
data = connection.get_configuration(filter=config_filter)
|
||||
|
||||
if isinstance(data, string_types):
|
||||
data = etree.fromstring(to_bytes(data, errors='surrogate_then_replace'))
|
||||
|
||||
resources = data.xpath('configuration/interfaces/interface')
|
||||
|
||||
objs = []
|
||||
if resources:
|
||||
objs = self.parse_l3_if_resources(resources)
|
||||
|
||||
config = []
|
||||
if objs:
|
||||
for l3_if_obj in objs:
|
||||
config.append(self.render_config(
|
||||
self.generated_spec, l3_if_obj))
|
||||
|
||||
facts = {}
|
||||
facts['l3_interfaces'] = config
|
||||
|
||||
ansible_facts['ansible_network_resources'].update(facts)
|
||||
return ansible_facts
|
||||
|
||||
def parse_l3_if_resources(self, l3_if_resources):
|
||||
l3_ifaces = []
|
||||
for iface in l3_if_resources:
|
||||
interface = {}
|
||||
interface['name'] = iface.find('name').text
|
||||
if iface.find('description') is not None:
|
||||
interface['description'] = iface.find('description').text
|
||||
interface['unit'] = iface.find('unit/name').text
|
||||
family = iface.find('unit/family/')
|
||||
if family is not None and family.tag != 'ethernet-switching':
|
||||
ipv4 = iface.findall('unit/family/inet/address')
|
||||
dhcp = iface.findall('unit/family/inet/dhcp')
|
||||
ipv6 = iface.findall('unit/family/inet6/address')
|
||||
if dhcp:
|
||||
interface['ipv4'] = [{'address': 'dhcp'}]
|
||||
if ipv4:
|
||||
interface['ipv4'] = self.get_ip_addresses(ipv4)
|
||||
elif not dhcp:
|
||||
interface['ipv4'] = None
|
||||
if ipv6:
|
||||
interface['ipv6'] = self.get_ip_addresses(ipv6)
|
||||
l3_ifaces.append(interface)
|
||||
return l3_ifaces
|
||||
|
||||
def get_ip_addresses(self, ip_addr):
|
||||
address_list = []
|
||||
for ip in ip_addr:
|
||||
for addr in ip:
|
||||
address_list.append({'address': addr.text})
|
||||
return address_list
|
||||
|
||||
def render_config(self, spec, conf):
|
||||
"""
|
||||
Render config as dictionary structure and delete keys
|
||||
from spec for null values
|
||||
|
||||
:param spec: The facts tree, generated from the argspec
|
||||
:param conf: The configuration
|
||||
:rtype: dictionary
|
||||
:returns: The generated config
|
||||
"""
|
||||
config = deepcopy(spec)
|
||||
config['name'] = conf['name']
|
||||
config['description'] = conf.get('description')
|
||||
config['unit'] = conf.get('unit', 0)
|
||||
config['ipv4'] = conf.get('ipv4')
|
||||
config['ipv6'] = conf.get('ipv6')
|
||||
|
||||
return utils.remove_empties(config)
|
@ -1,96 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos lacp fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
for a given resource, parsed, and the facts tree is populated
|
||||
based on the configuration.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.network.common import utils
|
||||
from ansible.module_utils.network.junos.argspec.lacp.lacp import LacpArgs
|
||||
from ansible.module_utils.network.junos.utils.utils import get_resource_config
|
||||
from ansible.module_utils.six import string_types
|
||||
try:
|
||||
from lxml import etree
|
||||
HAS_LXML = True
|
||||
except ImportError:
|
||||
HAS_LXML = False
|
||||
|
||||
|
||||
class LacpFacts(object):
|
||||
""" The junos lacp fact class
|
||||
"""
|
||||
|
||||
def __init__(self, module, subspec='config', options='options'):
|
||||
self._module = module
|
||||
self.argument_spec = LacpArgs.argument_spec
|
||||
spec = deepcopy(self.argument_spec)
|
||||
if subspec:
|
||||
if options:
|
||||
facts_argument_spec = spec[subspec][options]
|
||||
else:
|
||||
facts_argument_spec = spec[subspec]
|
||||
else:
|
||||
facts_argument_spec = spec
|
||||
|
||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
||||
|
||||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for interfaces
|
||||
:param connection: the device connection
|
||||
:param data: previously collected configuration as lxml ElementTree root instance
|
||||
or valid xml sting
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
if not HAS_LXML:
|
||||
self._module.fail_json(msg='lxml is not installed.')
|
||||
|
||||
if not data:
|
||||
config_filter = """
|
||||
<configuration>
|
||||
<chassis>
|
||||
<aggregated-devices>
|
||||
<ethernet>
|
||||
<lacp>
|
||||
</lacp>
|
||||
</ethernet>
|
||||
</aggregated-devices>
|
||||
</chassis>
|
||||
</configuration>
|
||||
"""
|
||||
data = get_resource_config(connection, config_filter=config_filter)
|
||||
|
||||
if isinstance(data, string_types):
|
||||
data = etree.fromstring(to_bytes(data, errors='surrogate_then_replace'))
|
||||
|
||||
facts = {}
|
||||
config = deepcopy(self.generated_spec)
|
||||
resources = data.xpath('configuration/chassis/aggregated-devices/ethernet/lacp')
|
||||
if resources:
|
||||
|
||||
lacp_root = resources[0]
|
||||
config['system_priority'] = utils.get_xml_conf_arg(lacp_root, 'system-priority')
|
||||
|
||||
if utils.get_xml_conf_arg(lacp_root, 'link-protection/non-revertive', data='tag'):
|
||||
config['link_protection'] = "non-revertive"
|
||||
|
||||
elif utils.get_xml_conf_arg(lacp_root, 'link-protection'):
|
||||
config['link_protection'] = "revertive"
|
||||
|
||||
params = utils.validate_config(self.argument_spec, {'config': utils.remove_empties(config)})
|
||||
facts['lacp'] = {}
|
||||
facts['lacp'].update(utils.remove_empties(params['config']))
|
||||
|
||||
ansible_facts['ansible_network_resources'].update(facts)
|
||||
|
||||
return ansible_facts
|
@ -1,113 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos lacp_interfaces fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
for a given resource, parsed, and the facts tree is populated
|
||||
based on the configuration.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.network.common import utils
|
||||
from ansible.module_utils.network.junos.argspec.lacp_interfaces.lacp_interfaces import Lacp_interfacesArgs
|
||||
from ansible.module_utils.network.junos.utils.utils import get_resource_config
|
||||
from ansible.module_utils.six import string_types
|
||||
try:
|
||||
from lxml import etree
|
||||
HAS_LXML = True
|
||||
except ImportError:
|
||||
HAS_LXML = False
|
||||
|
||||
|
||||
class Lacp_interfacesFacts(object):
|
||||
""" The junos lacp_interfaces fact class
|
||||
"""
|
||||
|
||||
def __init__(self, module, subspec='config', options='options'):
|
||||
self._module = module
|
||||
self.argument_spec = Lacp_interfacesArgs.argument_spec
|
||||
spec = deepcopy(self.argument_spec)
|
||||
if subspec:
|
||||
if options:
|
||||
facts_argument_spec = spec[subspec][options]
|
||||
else:
|
||||
facts_argument_spec = spec[subspec]
|
||||
else:
|
||||
facts_argument_spec = spec
|
||||
|
||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
||||
|
||||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for interfaces
|
||||
:param connection: the device connection
|
||||
:param data: previously collected configuration as lxml ElementTree root instance
|
||||
or valid xml sting
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
if not HAS_LXML:
|
||||
self._module.fail_json(msg='lxml is not installed.')
|
||||
|
||||
if not data:
|
||||
config_filter = """
|
||||
<configuration>
|
||||
<interfaces/>
|
||||
</configuration>
|
||||
"""
|
||||
data = get_resource_config(connection, config_filter=config_filter)
|
||||
|
||||
if isinstance(data, string_types):
|
||||
data = etree.fromstring(to_bytes(data, errors='surrogate_then_replace'))
|
||||
|
||||
self._resources = data.xpath('configuration/interfaces/interface')
|
||||
|
||||
objs = []
|
||||
for resource in self._resources:
|
||||
if resource is not None:
|
||||
obj = self.render_config(self.generated_spec, resource)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
facts = {}
|
||||
if objs:
|
||||
facts['lacp_interfaces'] = []
|
||||
params = utils.validate_config(self.argument_spec, {'config': objs})
|
||||
for cfg in params['config']:
|
||||
facts['lacp_interfaces'].append(utils.remove_empties(cfg))
|
||||
ansible_facts['ansible_network_resources'].update(facts)
|
||||
return ansible_facts
|
||||
|
||||
def render_config(self, spec, conf):
|
||||
"""
|
||||
Render config as dictionary structure and delete keys
|
||||
from spec for null values
|
||||
:param spec: The facts tree, generated from the argspec
|
||||
:param conf: The ElementTree instance of configuration object
|
||||
:rtype: dictionary
|
||||
:returns: The generated config
|
||||
"""
|
||||
config = deepcopy(spec)
|
||||
config['name'] = utils.get_xml_conf_arg(conf, 'name')
|
||||
config['period'] = utils.get_xml_conf_arg(conf, 'aggregated-ether-options/lacp/periodic')
|
||||
config['sync_reset'] = utils.get_xml_conf_arg(conf, 'aggregated-ether-options/lacp/sync-reset')
|
||||
force_up = utils.get_xml_conf_arg(conf, 'ether-options/ieee-802.3ad/lacp/force-up', data='tag')
|
||||
if force_up:
|
||||
config['force_up'] = True
|
||||
config['port_priority'] = utils.get_xml_conf_arg(conf, 'ether-options/ieee-802.3ad/lacp/port-priority')
|
||||
config['system']['priority'] = utils.get_xml_conf_arg(conf, 'aggregated-ether-options/lacp/system-priority')
|
||||
address = utils.get_xml_conf_arg(conf, 'aggregated-ether-options/lacp/system-id')
|
||||
if address:
|
||||
config['system'].update({'mac': {'address': address}})
|
||||
|
||||
lacp_intf_cfg = utils.remove_empties(config)
|
||||
# if lacp config is not present for interface return empty dict
|
||||
if len(lacp_intf_cfg) == 1:
|
||||
return {}
|
||||
else:
|
||||
return lacp_intf_cfg
|
@ -1,127 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos lag_interfaces fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
for a given resource, parsed, and the facts tree is populated
|
||||
based on the configuration.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.network.common import utils
|
||||
from ansible.module_utils.network.junos.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs
|
||||
from ansible.module_utils.network.junos.utils.utils import get_resource_config
|
||||
from ansible.module_utils.six import string_types
|
||||
try:
|
||||
from lxml import etree
|
||||
HAS_LXML = True
|
||||
except ImportError:
|
||||
HAS_LXML = False
|
||||
|
||||
|
||||
class Lag_interfacesFacts(object):
|
||||
""" The junos lag_interfaces fact class
|
||||
"""
|
||||
|
||||
def __init__(self, module, subspec='config', options='options'):
|
||||
self._module = module
|
||||
self.argument_spec = Lag_interfacesArgs.argument_spec
|
||||
spec = deepcopy(self.argument_spec)
|
||||
if subspec:
|
||||
if options:
|
||||
facts_argument_spec = spec[subspec][options]
|
||||
else:
|
||||
facts_argument_spec = spec[subspec]
|
||||
else:
|
||||
facts_argument_spec = spec
|
||||
|
||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
||||
|
||||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for interfaces
|
||||
:param connection: the device connection
|
||||
:param data: previously collected configuration as lxml ElementTree root instance
|
||||
or valid xml sting
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
if not HAS_LXML:
|
||||
self._module.fail_json(msg='lxml is not installed.')
|
||||
|
||||
if not data:
|
||||
config_filter = """
|
||||
<configuration>
|
||||
<interfaces/>
|
||||
</configuration>
|
||||
"""
|
||||
data = get_resource_config(connection, config_filter=config_filter)
|
||||
|
||||
if isinstance(data, string_types):
|
||||
data = etree.fromstring(to_bytes(data, errors='surrogate_then_replace'))
|
||||
|
||||
self._resources = data.xpath('configuration/interfaces/interface')
|
||||
|
||||
objs = []
|
||||
for resource in self._resources:
|
||||
if resource is not None:
|
||||
obj = self.render_config(self.generated_spec, resource)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
facts = {}
|
||||
if objs:
|
||||
facts['lag_interfaces'] = []
|
||||
params = utils.validate_config(self.argument_spec, {'config': objs})
|
||||
for cfg in params['config']:
|
||||
facts['lag_interfaces'].append(utils.remove_empties(cfg))
|
||||
ansible_facts['ansible_network_resources'].update(facts)
|
||||
return ansible_facts
|
||||
|
||||
def render_config(self, spec, conf):
|
||||
"""
|
||||
Render config as dictionary structure and delete keys
|
||||
from spec for null values
|
||||
:param spec: The facts tree, generated from the argspec
|
||||
:param conf: The ElementTree instance of configuration object
|
||||
:rtype: dictionary
|
||||
:returns: The generated config
|
||||
"""
|
||||
config = deepcopy(spec)
|
||||
intf_name = utils.get_xml_conf_arg(conf, 'name')
|
||||
if intf_name.startswith('ae'):
|
||||
config['name'] = intf_name
|
||||
config['members'] = []
|
||||
for interface_obj in self._resources:
|
||||
lag_interface_member = utils.get_xml_conf_arg(interface_obj, "ether-options/ieee-802.3ad[bundle='%s']/../../name" % intf_name)
|
||||
if lag_interface_member:
|
||||
member_config = {}
|
||||
member_config['member'] = lag_interface_member
|
||||
if utils.get_xml_conf_arg(interface_obj, "ether-options/ieee-802.3ad/primary", data='tag'):
|
||||
member_config['link_type'] = "primary"
|
||||
elif utils.get_xml_conf_arg(interface_obj, "ether-options/ieee-802.3ad/backup", data='tag'):
|
||||
member_config['link_type'] = "backup"
|
||||
|
||||
if member_config:
|
||||
config['members'].append(member_config)
|
||||
|
||||
for m in ['active', 'passive']:
|
||||
if utils.get_xml_conf_arg(conf, "aggregated-ether-options/lacp/%s" % m, data='tag'):
|
||||
config['mode'] = m
|
||||
break
|
||||
|
||||
link_protection = utils.get_xml_conf_arg(conf, "aggregated-ether-options/link-protection", data='tag')
|
||||
if link_protection:
|
||||
config['link_protection'] = True
|
||||
|
||||
lag_intf_cfg = utils.remove_empties(config)
|
||||
# if lag interfaces config is not present return empty dict
|
||||
if len(lag_intf_cfg) == 1:
|
||||
return {}
|
||||
else:
|
||||
return lag_intf_cfg
|
@ -1,195 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos interfaces fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
for a given resource, parsed, and the facts tree is populated
|
||||
based on the configuration.
|
||||
"""
|
||||
import platform
|
||||
|
||||
from ansible.module_utils.network.common.netconf import exec_rpc
|
||||
from ansible.module_utils.network.junos.junos import tostring
|
||||
from ansible.module_utils.network.junos.junos import get_configuration, get_capabilities, get_device
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
try:
|
||||
from lxml.etree import Element, SubElement
|
||||
except ImportError:
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
|
||||
|
||||
class FactsBase(object):
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.facts = dict()
|
||||
self.warnings = []
|
||||
|
||||
def populate(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def cli(self, command):
|
||||
reply = command(self.module, command)
|
||||
output = reply.find('.//output')
|
||||
if not output:
|
||||
self.module.fail_json(msg='failed to retrieve facts for command %s' % command)
|
||||
return to_text(output.text).strip()
|
||||
|
||||
def rpc(self, rpc):
|
||||
return exec_rpc(self.module, tostring(Element(rpc)))
|
||||
|
||||
def get_text(self, ele, tag):
|
||||
try:
|
||||
return to_text(ele.find(tag).text).strip()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
class Default(FactsBase):
|
||||
|
||||
def populate(self):
|
||||
self.facts.update(self.platform_facts())
|
||||
|
||||
reply = self.rpc('get-chassis-inventory')
|
||||
data = reply.find('.//chassis-inventory/chassis')
|
||||
self.facts['serialnum'] = self.get_text(data, 'serial-number')
|
||||
|
||||
def platform_facts(self):
|
||||
platform_facts = {}
|
||||
|
||||
resp = get_capabilities(self.module)
|
||||
device_info = resp['device_info']
|
||||
|
||||
platform_facts['system'] = device_info['network_os']
|
||||
|
||||
for item in ('model', 'image', 'version', 'platform', 'hostname'):
|
||||
val = device_info.get('network_os_%s' % item)
|
||||
if val:
|
||||
platform_facts[item] = val
|
||||
|
||||
platform_facts['api'] = resp['network_api']
|
||||
platform_facts['python_version'] = platform.python_version()
|
||||
|
||||
return platform_facts
|
||||
|
||||
|
||||
class Config(FactsBase):
|
||||
|
||||
def populate(self):
|
||||
config_format = self.module.params['config_format']
|
||||
reply = get_configuration(self.module, format=config_format)
|
||||
|
||||
if config_format == 'xml':
|
||||
config = tostring(reply.find('configuration')).strip()
|
||||
|
||||
elif config_format == 'text':
|
||||
config = self.get_text(reply, 'configuration-text')
|
||||
|
||||
elif config_format == 'json':
|
||||
config = self.module.from_json(reply.text.strip())
|
||||
|
||||
elif config_format == 'set':
|
||||
config = self.get_text(reply, 'configuration-set')
|
||||
|
||||
self.facts['config'] = config
|
||||
|
||||
|
||||
class Hardware(FactsBase):
|
||||
|
||||
def populate(self):
|
||||
|
||||
reply = self.rpc('get-system-memory-information')
|
||||
data = reply.find('.//system-memory-information/system-memory-summary-information')
|
||||
|
||||
self.facts.update({
|
||||
'memfree_mb': int(self.get_text(data, 'system-memory-free')),
|
||||
'memtotal_mb': int(self.get_text(data, 'system-memory-total'))
|
||||
})
|
||||
|
||||
reply = self.rpc('get-system-storage')
|
||||
data = reply.find('.//system-storage-information')
|
||||
|
||||
filesystems = list()
|
||||
for obj in data:
|
||||
filesystems.append(self.get_text(obj, 'filesystem-name'))
|
||||
self.facts['filesystems'] = filesystems
|
||||
|
||||
reply = self.rpc('get-route-engine-information')
|
||||
data = reply.find('.//route-engine-information')
|
||||
|
||||
routing_engines = dict()
|
||||
for obj in data:
|
||||
slot = self.get_text(obj, 'slot')
|
||||
routing_engines.update({slot: {}})
|
||||
routing_engines[slot].update({'slot': slot})
|
||||
for child in obj:
|
||||
if child.text != "\n":
|
||||
routing_engines[slot].update({child.tag.replace("-", "_"): child.text})
|
||||
|
||||
self.facts['routing_engines'] = routing_engines
|
||||
|
||||
if len(data) > 1:
|
||||
self.facts['has_2RE'] = True
|
||||
else:
|
||||
self.facts['has_2RE'] = False
|
||||
|
||||
reply = self.rpc('get-chassis-inventory')
|
||||
data = reply.findall('.//chassis-module')
|
||||
|
||||
modules = list()
|
||||
for obj in data:
|
||||
mod = dict()
|
||||
for child in obj:
|
||||
if child.text != "\n":
|
||||
mod.update({child.tag.replace("-", "_"): child.text})
|
||||
modules.append(mod)
|
||||
|
||||
self.facts['modules'] = modules
|
||||
|
||||
|
||||
class Interfaces(FactsBase):
|
||||
|
||||
def populate(self):
|
||||
ele = Element('get-interface-information')
|
||||
SubElement(ele, 'detail')
|
||||
reply = exec_rpc(self.module, tostring(ele))
|
||||
|
||||
interfaces = {}
|
||||
|
||||
for item in reply[0]:
|
||||
name = self.get_text(item, 'name')
|
||||
obj = {
|
||||
'oper-status': self.get_text(item, 'oper-status'),
|
||||
'admin-status': self.get_text(item, 'admin-status'),
|
||||
'speed': self.get_text(item, 'speed'),
|
||||
'macaddress': self.get_text(item, 'hardware-physical-address'),
|
||||
'mtu': self.get_text(item, 'mtu'),
|
||||
'type': self.get_text(item, 'if-type'),
|
||||
}
|
||||
|
||||
interfaces[name] = obj
|
||||
|
||||
self.facts['interfaces'] = interfaces
|
||||
|
||||
|
||||
class OFacts(FactsBase):
|
||||
def populate(self):
|
||||
|
||||
device = get_device(self.module)
|
||||
facts = dict(device.facts)
|
||||
|
||||
if '2RE' in facts:
|
||||
facts['has_2RE'] = facts['2RE']
|
||||
del facts['2RE']
|
||||
|
||||
facts['version_info'] = dict(facts['version_info'])
|
||||
if 'junos_info' in facts:
|
||||
for key, value in facts['junos_info'].items():
|
||||
if 'object' in value:
|
||||
value['object'] = dict(value['object'])
|
||||
|
||||
return facts
|
@ -1,89 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos lldp fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
for a given resource, parsed, and the facts tree is populated
|
||||
based on the configuration.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.network.common import utils
|
||||
from ansible.module_utils.network.junos.argspec.lldp_global.lldp_global import Lldp_globalArgs
|
||||
from ansible.module_utils.network.junos.utils.utils import get_resource_config
|
||||
from ansible.module_utils.six import string_types
|
||||
try:
|
||||
from lxml import etree
|
||||
HAS_LXML = True
|
||||
except ImportError:
|
||||
HAS_LXML = False
|
||||
|
||||
|
||||
class Lldp_globalFacts(object):
|
||||
""" The junos lldp fact class
|
||||
"""
|
||||
|
||||
def __init__(self, module, subspec='config', options='options'):
|
||||
self._module = module
|
||||
self.argument_spec = Lldp_globalArgs.argument_spec
|
||||
spec = deepcopy(self.argument_spec)
|
||||
if subspec:
|
||||
if options:
|
||||
facts_argument_spec = spec[subspec][options]
|
||||
else:
|
||||
facts_argument_spec = spec[subspec]
|
||||
else:
|
||||
facts_argument_spec = spec
|
||||
|
||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
||||
|
||||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for interfaces
|
||||
:param connection: the device connection
|
||||
:param data: previously collected configuration as lxml ElementTree root instance
|
||||
or valid xml sting
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
if not HAS_LXML:
|
||||
self._module.fail_json(msg='lxml is not installed.')
|
||||
|
||||
if not data:
|
||||
config_filter = """
|
||||
<configuration>
|
||||
<protocols>
|
||||
<lldp>
|
||||
</lldp>
|
||||
</protocols>
|
||||
</configuration>
|
||||
"""
|
||||
data = get_resource_config(connection, config_filter=config_filter)
|
||||
|
||||
if isinstance(data, string_types):
|
||||
data = etree.fromstring(to_bytes(data, errors='surrogate_then_replace'))
|
||||
|
||||
facts = {}
|
||||
config = deepcopy(self.generated_spec)
|
||||
resources = data.xpath('configuration/protocols/lldp')
|
||||
if resources:
|
||||
lldp_root = resources[0]
|
||||
config['address'] = utils.get_xml_conf_arg(lldp_root, 'management-address')
|
||||
config['interval'] = utils.get_xml_conf_arg(lldp_root, 'advertisement-interval')
|
||||
config['transmit_delay'] = utils.get_xml_conf_arg(lldp_root, 'transmit-delay')
|
||||
config['hold_multiplier'] = utils.get_xml_conf_arg(lldp_root, 'hold-multiplier')
|
||||
if utils.get_xml_conf_arg(lldp_root, 'disable', data='tag'):
|
||||
config['enable'] = False
|
||||
|
||||
params = utils.validate_config(self.argument_spec, {'config': utils.remove_empties(config)})
|
||||
|
||||
facts['lldp_global'] = utils.remove_empties(params['config'])
|
||||
ansible_facts['ansible_network_resources'].update(facts)
|
||||
|
||||
return ansible_facts
|
@ -1,104 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos lldp_interfaces fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
for a given resource, parsed, and the facts tree is populated
|
||||
based on the configuration.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.network.common import utils
|
||||
from ansible.module_utils.network.junos.argspec.lldp_interfaces.lldp_interfaces import Lldp_interfacesArgs
|
||||
from ansible.module_utils.network.junos.utils.utils import get_resource_config
|
||||
from ansible.module_utils.six import string_types
|
||||
try:
|
||||
from lxml import etree
|
||||
HAS_LXML = True
|
||||
except ImportError:
|
||||
HAS_LXML = False
|
||||
|
||||
|
||||
class Lldp_interfacesFacts(object):
|
||||
""" The junos lldp_interfaces fact class
|
||||
"""
|
||||
|
||||
def __init__(self, module, subspec='config', options='options'):
|
||||
self._module = module
|
||||
self.argument_spec = Lldp_interfacesArgs.argument_spec
|
||||
spec = deepcopy(self.argument_spec)
|
||||
if subspec:
|
||||
if options:
|
||||
facts_argument_spec = spec[subspec][options]
|
||||
else:
|
||||
facts_argument_spec = spec[subspec]
|
||||
else:
|
||||
facts_argument_spec = spec
|
||||
|
||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
||||
|
||||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for interfaces
|
||||
:param connection: the device connection
|
||||
:param data: previously collected configuration as lxml ElementTree root instance
|
||||
or valid xml sting
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
if not HAS_LXML:
|
||||
self._module.fail_json(msg='lxml is not installed.')
|
||||
|
||||
if not data:
|
||||
config_filter = """
|
||||
<configuration>
|
||||
<protocols>
|
||||
<lldp>
|
||||
<interface>
|
||||
</interface>
|
||||
</lldp>
|
||||
</protocols>
|
||||
</configuration>
|
||||
"""
|
||||
data = get_resource_config(connection, config_filter=config_filter)
|
||||
|
||||
if isinstance(data, string_types):
|
||||
data = etree.fromstring(to_bytes(data, errors='surrogate_then_replace'))
|
||||
|
||||
self._resources = data.xpath('configuration/protocols/lldp/interface')
|
||||
|
||||
objs = []
|
||||
for resource in self._resources:
|
||||
if resource is not None:
|
||||
obj = self.render_config(self.generated_spec, resource)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
facts = {}
|
||||
if objs:
|
||||
facts['lldp_interfaces'] = []
|
||||
params = utils.validate_config(self.argument_spec, {'config': objs})
|
||||
for cfg in params['config']:
|
||||
facts['lldp_interfaces'].append(utils.remove_empties(cfg))
|
||||
ansible_facts['ansible_network_resources'].update(facts)
|
||||
return ansible_facts
|
||||
|
||||
def render_config(self, spec, conf):
|
||||
"""
|
||||
Render config as dictionary structure and delete keys
|
||||
from spec for null values
|
||||
:param spec: The facts tree, generated from the argspec
|
||||
:param conf: The ElementTree instance of configuration object
|
||||
:rtype: dictionary
|
||||
:returns: The generated config
|
||||
"""
|
||||
config = deepcopy(spec)
|
||||
config['name'] = utils.get_xml_conf_arg(conf, 'name')
|
||||
if utils.get_xml_conf_arg(conf, 'disable', data='tag'):
|
||||
config['enabled'] = False
|
||||
return utils.remove_empties(config)
|
@ -1,152 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos static_routes fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
for a given resource, parsed, and the facts tree is populated
|
||||
based on the configuration.
|
||||
"""
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.network.common import utils
|
||||
from ansible.module_utils.network.junos.argspec.static_routes.static_routes import Static_routesArgs
|
||||
from ansible.module_utils.six import string_types
|
||||
try:
|
||||
from lxml import etree
|
||||
HAS_LXML = True
|
||||
except ImportError:
|
||||
HAS_LXML = False
|
||||
try:
|
||||
import xmltodict
|
||||
HAS_XMLTODICT = True
|
||||
except ImportError:
|
||||
HAS_XMLTODICT = False
|
||||
|
||||
|
||||
class Static_routesFacts(object):
|
||||
""" The junos static_routes fact class
|
||||
"""
|
||||
|
||||
def __init__(self, module, subspec='config', options='options'):
|
||||
self._module = module
|
||||
self.argument_spec = Static_routesArgs.argument_spec
|
||||
spec = deepcopy(self.argument_spec)
|
||||
if subspec:
|
||||
if options:
|
||||
facts_argument_spec = spec[subspec][options]
|
||||
else:
|
||||
facts_argument_spec = spec[subspec]
|
||||
else:
|
||||
facts_argument_spec = spec
|
||||
|
||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
||||
|
||||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for static_routes
|
||||
:param connection: the device connection
|
||||
:param ansible_facts: Facts dictionary
|
||||
:param data: previously collected conf
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
if not HAS_LXML:
|
||||
self._module.fail_json(msg='lxml is not installed.')
|
||||
if not HAS_XMLTODICT:
|
||||
self._module.fail_json(msg='xmltodict is not installed.')
|
||||
|
||||
if not data:
|
||||
config_filter = """
|
||||
<configuration>
|
||||
<routing-instances/>
|
||||
<routing-options/>
|
||||
</configuration>
|
||||
"""
|
||||
data = connection.get_configuration(filter=config_filter)
|
||||
|
||||
if isinstance(data, string_types):
|
||||
data = etree.fromstring(to_bytes(data,
|
||||
errors='surrogate_then_replace'))
|
||||
|
||||
resources = data.xpath('configuration/routing-options')
|
||||
vrf_resources = data.xpath('configuration/routing-instances')
|
||||
resources.extend(vrf_resources)
|
||||
|
||||
objs = []
|
||||
for resource in resources:
|
||||
if resource is not None:
|
||||
xml = self._get_xml_dict(resource)
|
||||
obj = self.render_config(self.generated_spec, xml)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
|
||||
facts = {}
|
||||
if objs:
|
||||
facts['static_routes'] = []
|
||||
params = utils.validate_config(self.argument_spec,
|
||||
{'config': objs})
|
||||
for cfg in params['config']:
|
||||
facts['static_routes'].append(utils.remove_empties(cfg))
|
||||
ansible_facts['ansible_network_resources'].update(facts)
|
||||
return ansible_facts
|
||||
|
||||
def _get_xml_dict(self, xml_root):
|
||||
xml_dict = xmltodict.parse(etree.tostring(xml_root), dict_constructor=dict)
|
||||
return xml_dict
|
||||
|
||||
def _create_route_dict(self, afi, route_path):
|
||||
routes_dict = {'afi': afi,
|
||||
'routes': []}
|
||||
if isinstance(route_path, dict):
|
||||
route_path = [route_path]
|
||||
for route in route_path:
|
||||
route_dict = {}
|
||||
route_dict['dest'] = route['name']
|
||||
if route.get('metric'):
|
||||
route_dict['metric'] = route['metric']['metric-value']
|
||||
route_dict['next_hop'] = []
|
||||
route_dict['next_hop'].append({'forward_router_address': route['next-hop']})
|
||||
routes_dict['routes'].append(route_dict)
|
||||
return routes_dict
|
||||
|
||||
def render_config(self, spec, conf):
|
||||
"""
|
||||
Render config as dictionary structure and delete keys
|
||||
from spec for null values
|
||||
|
||||
:param spec: The facts tree, generated from the argspec
|
||||
:param conf: The configuration
|
||||
:rtype: dictionary
|
||||
:returns: The generated config
|
||||
"""
|
||||
config = deepcopy(spec)
|
||||
routes = []
|
||||
config['address_families'] = []
|
||||
|
||||
if conf.get('routing-options'):
|
||||
if conf['routing-options'].get('rib'):
|
||||
if conf['routing-options'].get('rib').get('name') == 'inet6.0':
|
||||
if conf['routing-options'].get('rib').get('static'):
|
||||
route_path = conf['routing-options']['rib']['static'].get('route')
|
||||
routes.append(self._create_route_dict('ipv6', route_path))
|
||||
if conf['routing-options'].get('static'):
|
||||
route_path = conf['routing-options']['static'].get('route')
|
||||
routes.append(self._create_route_dict('ipv4', route_path))
|
||||
|
||||
if conf.get('routing-instances'):
|
||||
config['vrf'] = conf['routing-instances']['instance']['name']
|
||||
if conf['routing-instances'].get('instance').get('routing-options').get('rib').get('name') == config['vrf'] + '.inet6.0':
|
||||
if conf['routing-instances']['instance']['routing-options']['rib'].get('static'):
|
||||
route_path = conf['routing-instances']['instance']['routing-options']['rib']['static'].get('route')
|
||||
routes.append(self._create_route_dict('ipv6', route_path))
|
||||
if conf['routing-instances'].get('instance').get('routing-options').get('static'):
|
||||
route_path = conf['routing-instances']['instance']['routing-options']['static'].get('route')
|
||||
routes.append(self._create_route_dict('ipv4', route_path))
|
||||
config['address_families'].extend(routes)
|
||||
return utils.remove_empties(config)
|
@ -1,103 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos vlans fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
for a given resource, parsed, and the facts tree is populated
|
||||
based on the configuration.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils.network.common import utils
|
||||
from ansible.module_utils.network.junos.argspec.vlans.vlans import VlansArgs
|
||||
from ansible.module_utils.network.junos.utils.utils import get_resource_config
|
||||
from ansible.module_utils.six import string_types
|
||||
try:
|
||||
from lxml import etree
|
||||
HAS_LXML = True
|
||||
except ImportError:
|
||||
HAS_LXML = False
|
||||
|
||||
|
||||
class VlansFacts(object):
|
||||
""" The junos vlans fact class
|
||||
"""
|
||||
|
||||
def __init__(self, module, subspec='config', options='options'):
|
||||
self._module = module
|
||||
self.argument_spec = VlansArgs.argument_spec
|
||||
spec = deepcopy(self.argument_spec)
|
||||
if subspec:
|
||||
if options:
|
||||
facts_argument_spec = spec[subspec][options]
|
||||
else:
|
||||
facts_argument_spec = spec[subspec]
|
||||
else:
|
||||
facts_argument_spec = spec
|
||||
|
||||
self.generated_spec = utils.generate_dict(facts_argument_spec)
|
||||
|
||||
def populate_facts(self, connection, ansible_facts, data=None):
|
||||
""" Populate the facts for vlans
|
||||
:param connection: the device connection
|
||||
:param ansible_facts: Facts dictionary
|
||||
:param data: previously collected conf
|
||||
:rtype: dictionary
|
||||
:returns: facts
|
||||
"""
|
||||
if not HAS_LXML:
|
||||
self._module.fail_json(msg='lxml is not installed.')
|
||||
|
||||
if not data:
|
||||
config_filter = """
|
||||
<configuration>
|
||||
<vlans>
|
||||
</vlans>
|
||||
</configuration>
|
||||
"""
|
||||
data = get_resource_config(connection, config_filter=config_filter)
|
||||
|
||||
if isinstance(data, string_types):
|
||||
data = etree.fromstring(to_bytes(data,
|
||||
errors='surrogate_then_replace'))
|
||||
|
||||
resources = data.xpath('configuration/vlans/vlan')
|
||||
|
||||
objs = []
|
||||
for resource in resources:
|
||||
if resource is not None:
|
||||
obj = self.render_config(self.generated_spec, resource)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
facts = {}
|
||||
if objs:
|
||||
facts['vlans'] = []
|
||||
params = utils.validate_config(self.argument_spec,
|
||||
{'config': objs})
|
||||
for cfg in params['config']:
|
||||
facts['vlans'].append(utils.remove_empties(cfg))
|
||||
ansible_facts['ansible_network_resources'].update(facts)
|
||||
return ansible_facts
|
||||
|
||||
def render_config(self, spec, conf):
|
||||
"""
|
||||
Render config as dictionary structure and delete keys
|
||||
from spec for null values
|
||||
|
||||
:param spec: The facts tree, generated from the argspec
|
||||
:param conf: The configuration
|
||||
:rtype: dictionary
|
||||
:returns: The generated config
|
||||
"""
|
||||
config = deepcopy(spec)
|
||||
config['name'] = utils.get_xml_conf_arg(conf, 'name')
|
||||
config['vlan_id'] = utils.get_xml_conf_arg(conf, 'vlan-id')
|
||||
config['description'] = utils.get_xml_conf_arg(conf, 'description')
|
||||
return utils.remove_empties(config)
|
@ -1,465 +0,0 @@
|
||||
#
|
||||
# (c) 2017 Red Hat, Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# 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/>.
|
||||
#
|
||||
import collections
|
||||
import json
|
||||
from contextlib import contextmanager
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils.basic import env_fallback
|
||||
from ansible.module_utils.connection import Connection, ConnectionError
|
||||
from ansible.module_utils.network.common.netconf import NetconfConnection
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
try:
|
||||
from lxml.etree import Element, SubElement, tostring as xml_to_string
|
||||
HAS_LXML = True
|
||||
except ImportError:
|
||||
from xml.etree.ElementTree import Element, SubElement, tostring as xml_to_string
|
||||
HAS_LXML = False
|
||||
|
||||
try:
|
||||
from jnpr.junos import Device
|
||||
from jnpr.junos.exception import ConnectError
|
||||
HAS_PYEZ = True
|
||||
except ImportError:
|
||||
HAS_PYEZ = False
|
||||
|
||||
ACTIONS = frozenset(['merge', 'override', 'replace', 'update', 'set'])
|
||||
JSON_ACTIONS = frozenset(['merge', 'override', 'update'])
|
||||
FORMATS = frozenset(['xml', 'text', 'json'])
|
||||
CONFIG_FORMATS = frozenset(['xml', 'text', 'json', 'set'])
|
||||
|
||||
junos_provider_spec = {
|
||||
'host': dict(),
|
||||
'port': dict(type='int'),
|
||||
'username': dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])),
|
||||
'password': dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True),
|
||||
'ssh_keyfile': dict(fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE']), type='path'),
|
||||
'timeout': dict(type='int'),
|
||||
'transport': dict(default='netconf', choices=['cli', 'netconf'])
|
||||
}
|
||||
junos_argument_spec = {
|
||||
'provider': dict(type='dict', options=junos_provider_spec, removed_in_version=2.14),
|
||||
}
|
||||
|
||||
|
||||
def tostring(element, encoding='UTF-8'):
|
||||
if HAS_LXML:
|
||||
return xml_to_string(element, encoding='unicode')
|
||||
else:
|
||||
return to_text(xml_to_string(element, encoding), encoding=encoding)
|
||||
|
||||
|
||||
def get_provider_argspec():
|
||||
return junos_provider_spec
|
||||
|
||||
|
||||
def get_connection(module):
|
||||
if hasattr(module, '_junos_connection'):
|
||||
return module._junos_connection
|
||||
|
||||
capabilities = get_capabilities(module)
|
||||
network_api = capabilities.get('network_api')
|
||||
if network_api == 'cliconf':
|
||||
module._junos_connection = Connection(module._socket_path)
|
||||
elif network_api == 'netconf':
|
||||
module._junos_connection = NetconfConnection(module._socket_path)
|
||||
else:
|
||||
module.fail_json(msg='Invalid connection type %s' % network_api)
|
||||
|
||||
return module._junos_connection
|
||||
|
||||
|
||||
def get_capabilities(module):
|
||||
if hasattr(module, '_junos_capabilities'):
|
||||
return module._junos_capabilities
|
||||
|
||||
try:
|
||||
capabilities = Connection(module._socket_path).get_capabilities()
|
||||
except ConnectionError as exc:
|
||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
||||
module._junos_capabilities = json.loads(capabilities)
|
||||
return module._junos_capabilities
|
||||
|
||||
|
||||
def get_device(module):
|
||||
provider = module.params.get("provider") or {}
|
||||
host = provider.get('host')
|
||||
|
||||
kwargs = {
|
||||
'port': provider.get('port') or 830,
|
||||
'user': provider.get('username')
|
||||
}
|
||||
|
||||
if 'password' in provider:
|
||||
kwargs['passwd'] = provider.get('password')
|
||||
|
||||
if 'ssh_keyfile' in provider:
|
||||
kwargs['ssh_private_key_file'] = provider.get('ssh_keyfile')
|
||||
|
||||
if module.params.get('ssh_config'):
|
||||
kwargs['ssh_config'] = module.params['ssh_config']
|
||||
|
||||
if module.params.get('ssh_private_key_file'):
|
||||
kwargs['ssh_private_key_file'] = module.params['ssh_private_key_file']
|
||||
|
||||
kwargs['gather_facts'] = False
|
||||
|
||||
try:
|
||||
device = Device(host, **kwargs)
|
||||
device.open()
|
||||
device.timeout = provider.get('timeout') or 10
|
||||
except ConnectError as exc:
|
||||
module.fail_json('unable to connect to %s: %s' % (host, to_text(exc)))
|
||||
|
||||
return device
|
||||
|
||||
|
||||
def is_netconf(module):
|
||||
capabilities = get_capabilities(module)
|
||||
return True if capabilities.get('network_api') == 'netconf' else False
|
||||
|
||||
|
||||
def _validate_rollback_id(module, value):
|
||||
try:
|
||||
if not 0 <= int(value) <= 49:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
module.fail_json(msg='rollback must be between 0 and 49')
|
||||
|
||||
|
||||
def load_configuration(module, candidate=None, action='merge', rollback=None, format='xml'):
|
||||
|
||||
if all((candidate is None, rollback is None)):
|
||||
module.fail_json(msg='one of candidate or rollback must be specified')
|
||||
|
||||
elif all((candidate is not None, rollback is not None)):
|
||||
module.fail_json(msg='candidate and rollback are mutually exclusive')
|
||||
|
||||
if format not in FORMATS:
|
||||
module.fail_json(msg='invalid format specified')
|
||||
|
||||
if format == 'json' and action not in JSON_ACTIONS:
|
||||
module.fail_json(msg='invalid action for format json')
|
||||
elif format in ('text', 'xml') and action not in ACTIONS:
|
||||
module.fail_json(msg='invalid action format %s' % format)
|
||||
if action == 'set' and format != 'text':
|
||||
module.fail_json(msg='format must be text when action is set')
|
||||
|
||||
conn = get_connection(module)
|
||||
try:
|
||||
if rollback is not None:
|
||||
_validate_rollback_id(module, rollback)
|
||||
obj = Element('load-configuration', {'rollback': str(rollback)})
|
||||
conn.execute_rpc(tostring(obj))
|
||||
else:
|
||||
return conn.load_configuration(config=candidate, action=action, format=format)
|
||||
except ConnectionError as exc:
|
||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
||||
|
||||
|
||||
def get_configuration(module, compare=False, format='xml', rollback='0', filter=None):
|
||||
if format not in CONFIG_FORMATS:
|
||||
module.fail_json(msg='invalid config format specified')
|
||||
|
||||
conn = get_connection(module)
|
||||
try:
|
||||
if compare:
|
||||
xattrs = {'format': format}
|
||||
_validate_rollback_id(module, rollback)
|
||||
xattrs['compare'] = 'rollback'
|
||||
xattrs['rollback'] = str(rollback)
|
||||
reply = conn.execute_rpc(tostring(Element('get-configuration', xattrs)))
|
||||
else:
|
||||
reply = conn.get_configuration(format=format, filter=filter)
|
||||
except ConnectionError as exc:
|
||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
||||
return reply
|
||||
|
||||
|
||||
def commit_configuration(module, confirm=False, check=False, comment=None, confirm_timeout=None, synchronize=False, at_time=None):
|
||||
conn = get_connection(module)
|
||||
try:
|
||||
if check:
|
||||
reply = conn.validate()
|
||||
else:
|
||||
if is_netconf(module):
|
||||
reply = conn.commit(confirmed=confirm, timeout=confirm_timeout, comment=comment, synchronize=synchronize, at_time=at_time)
|
||||
else:
|
||||
reply = conn.commit(comment=comment, confirmed=confirm, at_time=at_time, synchronize=synchronize)
|
||||
except ConnectionError as exc:
|
||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
||||
return reply
|
||||
|
||||
|
||||
def command(module, cmd, format='text', rpc_only=False):
|
||||
conn = get_connection(module)
|
||||
if rpc_only:
|
||||
cmd += ' | display xml rpc'
|
||||
try:
|
||||
response = conn.command(command=cmd, format=format)
|
||||
except ConnectionError as exc:
|
||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
||||
return response
|
||||
|
||||
|
||||
def lock_configuration(module):
|
||||
conn = get_connection(module)
|
||||
try:
|
||||
response = conn.lock()
|
||||
except ConnectionError as exc:
|
||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
||||
return response
|
||||
|
||||
|
||||
def unlock_configuration(module):
|
||||
conn = get_connection(module)
|
||||
try:
|
||||
response = conn.unlock()
|
||||
except ConnectionError as exc:
|
||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
||||
return response
|
||||
|
||||
|
||||
@contextmanager
|
||||
def locked_config(module):
|
||||
try:
|
||||
lock_configuration(module)
|
||||
yield
|
||||
finally:
|
||||
unlock_configuration(module)
|
||||
|
||||
|
||||
def discard_changes(module):
|
||||
conn = get_connection(module)
|
||||
try:
|
||||
response = conn.discard_changes()
|
||||
except ConnectionError as exc:
|
||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
||||
return response
|
||||
|
||||
|
||||
def get_diff(module, rollback='0'):
|
||||
reply = get_configuration(module, compare=True, format='text', rollback=rollback)
|
||||
# if warning is received from device diff is empty.
|
||||
if isinstance(reply, list):
|
||||
return None
|
||||
|
||||
output = reply.find('.//configuration-output')
|
||||
if output is not None:
|
||||
return to_text(output.text, encoding='latin-1').strip()
|
||||
|
||||
|
||||
def load_config(module, candidate, warnings, action='merge', format='xml'):
|
||||
get_connection(module)
|
||||
if not candidate:
|
||||
return
|
||||
|
||||
if isinstance(candidate, list):
|
||||
candidate = '\n'.join(candidate)
|
||||
|
||||
reply = load_configuration(module, candidate, action=action, format=format)
|
||||
if isinstance(reply, list):
|
||||
warnings.extend(reply)
|
||||
|
||||
module._junos_connection.validate()
|
||||
return get_diff(module)
|
||||
|
||||
|
||||
def map_params_to_obj(module, param_to_xpath_map, param=None):
|
||||
"""
|
||||
Creates a new dictionary with key as xpath corresponding
|
||||
to param and value is a list of dict with metadata and values for
|
||||
the xpath.
|
||||
Acceptable metadata keys:
|
||||
'value': Value of param.
|
||||
'tag_only': Value is indicated by tag only in xml hierarchy.
|
||||
'leaf_only': If operation is to be added at leaf node only.
|
||||
'value_req': If value(text) is required for leaf node.
|
||||
'is_key': If the field is key or not.
|
||||
eg: Output
|
||||
{
|
||||
'name': [{'value': 'ge-0/0/1'}]
|
||||
'disable': [{'value': True, tag_only': True}]
|
||||
}
|
||||
|
||||
:param module:
|
||||
:param param_to_xpath_map: Modules params to xpath map
|
||||
:return: obj
|
||||
"""
|
||||
if not param:
|
||||
param = module.params
|
||||
|
||||
obj = collections.OrderedDict()
|
||||
for key, attribute in param_to_xpath_map.items():
|
||||
if key in param:
|
||||
is_attribute_dict = False
|
||||
|
||||
value = param[key]
|
||||
if not isinstance(value, (list, tuple)):
|
||||
value = [value]
|
||||
|
||||
if isinstance(attribute, dict):
|
||||
xpath = attribute.get('xpath')
|
||||
is_attribute_dict = True
|
||||
else:
|
||||
xpath = attribute
|
||||
|
||||
if not obj.get(xpath):
|
||||
obj[xpath] = list()
|
||||
|
||||
for val in value:
|
||||
if is_attribute_dict:
|
||||
attr = deepcopy(attribute)
|
||||
del attr['xpath']
|
||||
|
||||
attr.update({'value': val})
|
||||
obj[xpath].append(attr)
|
||||
else:
|
||||
obj[xpath].append({'value': val})
|
||||
return obj
|
||||
|
||||
|
||||
def map_obj_to_ele(module, want, top, value_map=None, param=None):
|
||||
if not HAS_LXML:
|
||||
module.fail_json(msg='lxml is not installed.')
|
||||
|
||||
if not param:
|
||||
param = module.params
|
||||
|
||||
root = Element('root')
|
||||
top_ele = top.split('/')
|
||||
ele = SubElement(root, top_ele[0])
|
||||
|
||||
if len(top_ele) > 1:
|
||||
for item in top_ele[1:-1]:
|
||||
ele = SubElement(ele, item)
|
||||
container = ele
|
||||
state = param.get('state')
|
||||
active = param.get('active')
|
||||
if active:
|
||||
oper = 'active'
|
||||
else:
|
||||
oper = 'inactive'
|
||||
|
||||
# build xml subtree
|
||||
if container.tag != top_ele[-1]:
|
||||
node = SubElement(container, top_ele[-1])
|
||||
else:
|
||||
node = container
|
||||
|
||||
for fxpath, attributes in want.items():
|
||||
for attr in attributes:
|
||||
tag_only = attr.get('tag_only', False)
|
||||
leaf_only = attr.get('leaf_only', False)
|
||||
value_req = attr.get('value_req', False)
|
||||
is_key = attr.get('is_key', False)
|
||||
parent_attrib = attr.get('parent_attrib', True)
|
||||
value = attr.get('value')
|
||||
field_top = attr.get('top')
|
||||
|
||||
# operation 'delete' is added as element attribute
|
||||
# only if it is key or leaf only node
|
||||
if state == 'absent' and not (is_key or leaf_only):
|
||||
continue
|
||||
|
||||
# convert param value to device specific value
|
||||
if value_map and fxpath in value_map:
|
||||
value = value_map[fxpath].get(value)
|
||||
|
||||
if (value is not None) or tag_only or leaf_only:
|
||||
ele = node
|
||||
if field_top:
|
||||
# eg: top = 'system/syslog/file'
|
||||
# field_top = 'system/syslog/file/contents'
|
||||
# <file>
|
||||
# <name>test</name>
|
||||
# <contents>
|
||||
# </contents>
|
||||
# </file>
|
||||
ele_list = root.xpath(top + '/' + field_top)
|
||||
|
||||
if not len(ele_list):
|
||||
fields = field_top.split('/')
|
||||
ele = node
|
||||
for item in fields:
|
||||
inner_ele = root.xpath(top + '/' + item)
|
||||
if len(inner_ele):
|
||||
ele = inner_ele[0]
|
||||
else:
|
||||
ele = SubElement(ele, item)
|
||||
else:
|
||||
ele = ele_list[0]
|
||||
|
||||
if value is not None and not isinstance(value, bool):
|
||||
value = to_text(value, errors='surrogate_then_replace')
|
||||
|
||||
if fxpath:
|
||||
tags = fxpath.split('/')
|
||||
for item in tags:
|
||||
ele = SubElement(ele, item)
|
||||
|
||||
if tag_only:
|
||||
if state == 'present':
|
||||
if not value:
|
||||
# if value of tag_only node is false, delete the node
|
||||
ele.set('delete', 'delete')
|
||||
|
||||
elif leaf_only:
|
||||
if state == 'present':
|
||||
ele.set(oper, oper)
|
||||
ele.text = value
|
||||
else:
|
||||
ele.set('delete', 'delete')
|
||||
# Add value of leaf node if required while deleting.
|
||||
# in some cases if value is present while deleting, it
|
||||
# can result in error, hence the check
|
||||
if value_req:
|
||||
ele.text = value
|
||||
if is_key:
|
||||
par = ele.getparent()
|
||||
par.set('delete', 'delete')
|
||||
else:
|
||||
ele.text = value
|
||||
par = ele.getparent()
|
||||
|
||||
if parent_attrib:
|
||||
if state == 'present':
|
||||
# set replace attribute at parent node
|
||||
if not par.attrib.get('replace'):
|
||||
par.set('replace', 'replace')
|
||||
|
||||
# set active/inactive at parent node
|
||||
if not par.attrib.get(oper):
|
||||
par.set(oper, oper)
|
||||
else:
|
||||
par.set('delete', 'delete')
|
||||
|
||||
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]
|
@ -1,28 +0,0 @@
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# utils
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.network.junos.junos import tostring
|
||||
try:
|
||||
from ncclient.xml_ import new_ele, to_ele
|
||||
HAS_NCCLIENT = True
|
||||
except ImportError:
|
||||
HAS_NCCLIENT = False
|
||||
|
||||
|
||||
def get_resource_config(connection, config_filter=None, attrib=None):
|
||||
|
||||
if attrib is None:
|
||||
attrib = {'inherit': 'inherit'}
|
||||
|
||||
get_ele = new_ele('get-configuration', attrib)
|
||||
if config_filter:
|
||||
get_ele.append(to_ele(config_filter))
|
||||
|
||||
return connection.execute_rpc(tostring(get_ele))
|
@ -1,392 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_interface
|
||||
version_added: "2.4"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
short_description: Manage Interface on Juniper JUNOS network devices
|
||||
description:
|
||||
- This module provides declarative management of Interfaces
|
||||
on Juniper JUNOS network devices.
|
||||
deprecated:
|
||||
removed_in: "2.13"
|
||||
why: Updated modules released with more functionality
|
||||
alternative: Use M(junos_interfaces) instead.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the Interface.
|
||||
required: true
|
||||
description:
|
||||
description:
|
||||
- Description of Interface.
|
||||
enabled:
|
||||
description:
|
||||
- Configure interface link status.
|
||||
type: bool
|
||||
speed:
|
||||
description:
|
||||
- Interface link speed.
|
||||
mtu:
|
||||
description:
|
||||
- Maximum size of transmit packet.
|
||||
duplex:
|
||||
description:
|
||||
- Interface link status.
|
||||
default: auto
|
||||
choices: ['full', 'half', 'auto']
|
||||
tx_rate:
|
||||
description:
|
||||
- Transmit rate in bits per second (bps).
|
||||
- This is state check parameter only.
|
||||
- Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
|
||||
rx_rate:
|
||||
description:
|
||||
- Receiver rate in bits per second (bps).
|
||||
- This is state check parameter only.
|
||||
- Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
|
||||
neighbors:
|
||||
description:
|
||||
- Check the operational state of given interface C(name) for LLDP neighbor.
|
||||
- The following suboptions are available.
|
||||
suboptions:
|
||||
host:
|
||||
description:
|
||||
- "LLDP neighbor host for given interface C(name)."
|
||||
port:
|
||||
description:
|
||||
- "LLDP neighbor port to which given interface C(name) is connected."
|
||||
delay:
|
||||
description:
|
||||
- Time in seconds to wait before checking for the operational state on remote
|
||||
device. This wait is applicable for operational state argument which are
|
||||
I(state) with values C(up)/C(down), I(tx_rate) and I(rx_rate).
|
||||
default: 10
|
||||
aggregate:
|
||||
description: List of Interfaces definitions.
|
||||
state:
|
||||
description:
|
||||
- State of the Interface configuration, C(up) indicates present and
|
||||
operationally up and C(down) indicates present and operationally C(down)
|
||||
default: present
|
||||
choices: ['present', 'absent', 'up', 'down']
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
default: True
|
||||
type: bool
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure interface
|
||||
junos_interface:
|
||||
name: ge-0/0/1
|
||||
description: test-interface
|
||||
|
||||
- name: remove interface
|
||||
junos_interface:
|
||||
name: ge-0/0/1
|
||||
state: absent
|
||||
|
||||
- name: make interface down
|
||||
junos_interface:
|
||||
name: ge-0/0/1
|
||||
enabled: False
|
||||
|
||||
- name: make interface up
|
||||
junos_interface:
|
||||
name: ge-0/0/1
|
||||
enabled: True
|
||||
|
||||
- name: Deactivate interface config
|
||||
junos_interface:
|
||||
name: ge-0/0/1
|
||||
state: present
|
||||
active: False
|
||||
|
||||
- name: Activate interface config
|
||||
net_interface:
|
||||
name: ge-0/0/1
|
||||
state: present
|
||||
active: True
|
||||
|
||||
- name: Configure interface speed, mtu, duplex
|
||||
junos_interface:
|
||||
name: ge-0/0/1
|
||||
state: present
|
||||
speed: 1g
|
||||
mtu: 256
|
||||
duplex: full
|
||||
|
||||
- name: Create interface using aggregate
|
||||
junos_interface:
|
||||
aggregate:
|
||||
- name: ge-0/0/1
|
||||
description: test-interface-1
|
||||
- name: ge-0/0/2
|
||||
description: test-interface-2
|
||||
speed: 1g
|
||||
duplex: full
|
||||
mtu: 512
|
||||
|
||||
- name: Delete interface using aggregate
|
||||
junos_interface:
|
||||
aggregate:
|
||||
- name: ge-0/0/1
|
||||
- name: ge-0/0/2
|
||||
state: absent
|
||||
|
||||
- name: Check intent arguments
|
||||
junos_interface:
|
||||
name: "{{ name }}"
|
||||
state: up
|
||||
tx_rate: ge(0)
|
||||
rx_rate: le(0)
|
||||
|
||||
- name: Check neighbor intent
|
||||
junos_interface:
|
||||
name: xe-0/1/1
|
||||
neighbors:
|
||||
- port: Ethernet1/0/1
|
||||
host: netdev
|
||||
|
||||
- name: Config + intent
|
||||
junos_interface:
|
||||
name: "{{ name }}"
|
||||
enabled: False
|
||||
state: down
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
diff.prepared:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit interfaces]
|
||||
+ ge-0/0/1 {
|
||||
+ description test-interface;
|
||||
+ }
|
||||
"""
|
||||
import collections
|
||||
|
||||
from copy import deepcopy
|
||||
from time import sleep
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.common.netconf import exec_rpc
|
||||
from ansible.module_utils.network.common.utils import remove_default_spec
|
||||
from ansible.module_utils.network.common.utils import conditional
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, tostring
|
||||
from ansible.module_utils.network.junos.junos import load_config, map_params_to_obj, map_obj_to_ele
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config, to_param_list
|
||||
|
||||
try:
|
||||
from lxml.etree import Element, SubElement
|
||||
except ImportError:
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def validate_mtu(value, module):
|
||||
if value and not 256 <= value <= 9192:
|
||||
module.fail_json(msg='mtu must be between 256 and 9192')
|
||||
|
||||
|
||||
def validate_param_values(module, obj, param=None):
|
||||
if not param:
|
||||
param = module.params
|
||||
for key in obj:
|
||||
# validate the param value (if validator func exists)
|
||||
validator = globals().get('validate_%s' % key)
|
||||
if callable(validator):
|
||||
validator(param.get(key), module)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
neighbors_spec = dict(
|
||||
host=dict(),
|
||||
port=dict()
|
||||
)
|
||||
|
||||
element_spec = dict(
|
||||
name=dict(),
|
||||
description=dict(),
|
||||
enabled=dict(default=True, type='bool'),
|
||||
speed=dict(),
|
||||
mtu=dict(type='int'),
|
||||
duplex=dict(choices=['full', 'half', 'auto']),
|
||||
tx_rate=dict(),
|
||||
rx_rate=dict(),
|
||||
neighbors=dict(type='list', elements='dict', options=neighbors_spec),
|
||||
delay=dict(default=10, type='int'),
|
||||
state=dict(default='present', choices=['present', 'absent', 'up', 'down']),
|
||||
active=dict(default=True, type='bool')
|
||||
)
|
||||
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
|
||||
)
|
||||
|
||||
argument_spec.update(element_spec)
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
required_one_of = [['name', 'aggregate']]
|
||||
mutually_exclusive = [['name', 'aggregate']]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False}
|
||||
|
||||
if warnings:
|
||||
result['warnings'] = warnings
|
||||
|
||||
top = 'interfaces/interface'
|
||||
|
||||
param_to_xpath_map = collections.OrderedDict()
|
||||
param_to_xpath_map.update([
|
||||
('name', {'xpath': 'name', 'is_key': True}),
|
||||
('description', 'description'),
|
||||
('speed', 'speed'),
|
||||
('mtu', 'mtu'),
|
||||
('duplex', 'link-mode'),
|
||||
('disable', {'xpath': 'disable', 'tag_only': True})
|
||||
])
|
||||
|
||||
choice_to_value_map = {
|
||||
'link-mode': {'full': 'full-duplex', 'half': 'half-duplex', 'auto': 'automatic'}
|
||||
}
|
||||
|
||||
params = to_param_list(module)
|
||||
|
||||
requests = list()
|
||||
for param in params:
|
||||
# if key doesn't exist in the item, get it from module.params
|
||||
for key in param:
|
||||
if param.get(key) is None:
|
||||
param[key] = module.params[key]
|
||||
|
||||
item = param.copy()
|
||||
state = item.get('state')
|
||||
item['disable'] = True if not item.get('enabled') else False
|
||||
|
||||
if state in ('present', 'up', 'down'):
|
||||
item['state'] = 'present'
|
||||
|
||||
validate_param_values(module, param_to_xpath_map, param=item)
|
||||
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):
|
||||
for req in requests:
|
||||
diff = load_config(module, tostring(req), warnings, action='merge')
|
||||
|
||||
# issue commit after last configuration change is done
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
failed_conditions = []
|
||||
neighbors = None
|
||||
for item in params:
|
||||
state = item.get('state')
|
||||
tx_rate = item.get('tx_rate')
|
||||
rx_rate = item.get('rx_rate')
|
||||
want_neighbors = item.get('neighbors')
|
||||
|
||||
if state not in ('up', 'down') and tx_rate is None and rx_rate is None and want_neighbors is None:
|
||||
continue
|
||||
|
||||
element = Element('get-interface-information')
|
||||
intf_name = SubElement(element, 'interface-name')
|
||||
intf_name.text = item.get('name')
|
||||
|
||||
if result['changed']:
|
||||
sleep(item.get('delay'))
|
||||
|
||||
reply = exec_rpc(module, tostring(element), ignore_warning=False)
|
||||
if state in ('up', 'down'):
|
||||
admin_status = reply.xpath('interface-information/physical-interface/admin-status')
|
||||
if not admin_status or not conditional(state, admin_status[0].text.strip()):
|
||||
failed_conditions.append('state ' + 'eq(%s)' % state)
|
||||
|
||||
if tx_rate:
|
||||
output_bps = reply.xpath('interface-information/physical-interface/traffic-statistics/output-bps')
|
||||
if not output_bps or not conditional(tx_rate, output_bps[0].text.strip(), cast=int):
|
||||
failed_conditions.append('tx_rate ' + tx_rate)
|
||||
|
||||
if rx_rate:
|
||||
input_bps = reply.xpath('interface-information/physical-interface/traffic-statistics/input-bps')
|
||||
if not input_bps or not conditional(rx_rate, input_bps[0].text.strip(), cast=int):
|
||||
failed_conditions.append('rx_rate ' + rx_rate)
|
||||
|
||||
if want_neighbors:
|
||||
if neighbors is None:
|
||||
element = Element('get-lldp-interface-neighbors')
|
||||
intf_name = SubElement(element, 'interface-device')
|
||||
intf_name.text = item.get('name')
|
||||
|
||||
reply = exec_rpc(module, tostring(element), ignore_warning=False)
|
||||
have_host = [item.text for item in reply.xpath('lldp-neighbors-information/lldp-neighbor-information/lldp-remote-system-name')]
|
||||
have_port = [item.text for item in reply.xpath('lldp-neighbors-information/lldp-neighbor-information/lldp-remote-port-id')]
|
||||
|
||||
for neighbor in want_neighbors:
|
||||
host = neighbor.get('host')
|
||||
port = neighbor.get('port')
|
||||
if host and host not in have_host:
|
||||
failed_conditions.append('host ' + host)
|
||||
if port and port not in have_port:
|
||||
failed_conditions.append('port ' + port)
|
||||
if failed_conditions:
|
||||
msg = 'One or more conditional statements have not been satisfied'
|
||||
module.fail_json(msg=msg, failed_conditions=failed_conditions)
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,287 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_l2_interface
|
||||
version_added: "2.5"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
short_description: Manage Layer-2 interface on Juniper JUNOS network devices
|
||||
description:
|
||||
- This module provides declarative management of Layer-2 interface
|
||||
on Juniper JUNOS network devices.
|
||||
deprecated:
|
||||
removed_in: "2.13"
|
||||
why: Updated modules released with more functionality
|
||||
alternative: Use M(junos_l2_interfaces) instead.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the interface excluding any logical unit number.
|
||||
description:
|
||||
description:
|
||||
- Description of Interface.
|
||||
aggregate:
|
||||
description:
|
||||
- List of Layer-2 interface definitions.
|
||||
mode:
|
||||
description:
|
||||
- Mode in which interface needs to be configured.
|
||||
choices: ['access', 'trunk']
|
||||
access_vlan:
|
||||
description:
|
||||
- Configure given VLAN in access port. The value of C(access_vlan) should
|
||||
be vlan name.
|
||||
trunk_vlans:
|
||||
description:
|
||||
- List of VLAN names to be configured in trunk port. The value of C(trunk_vlans) should
|
||||
be list of vlan names.
|
||||
native_vlan:
|
||||
description:
|
||||
- Native VLAN to be configured in trunk port. The value of C(native_vlan)
|
||||
should be vlan id.
|
||||
enhanced_layer:
|
||||
description:
|
||||
- True if your device has Enhanced Layer 2 Software (ELS).
|
||||
default: True
|
||||
type: bool
|
||||
version_added: "2.7"
|
||||
unit:
|
||||
description:
|
||||
- Logical interface number. Value of C(unit) should be of type
|
||||
integer.
|
||||
default: 0
|
||||
filter_input:
|
||||
description:
|
||||
- The name of input filter of ethernet-switching.
|
||||
version_added: "2.8"
|
||||
filter_output:
|
||||
description:
|
||||
- The name of output filter of ethernet-switching.
|
||||
version_added: "2.8"
|
||||
state:
|
||||
description:
|
||||
- State of the Layer-2 Interface configuration.
|
||||
default: present
|
||||
choices: ['present', 'absent',]
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
default: True
|
||||
type: bool
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Configure interface in access mode
|
||||
junos_l2_interface:
|
||||
name: ge-0/0/1
|
||||
description: interface-access
|
||||
mode: access
|
||||
access_vlan: red
|
||||
active: True
|
||||
state: present
|
||||
|
||||
- name: Configure interface in trunk mode
|
||||
junos_l2_interface:
|
||||
name: ge-0/0/1
|
||||
description: interface-trunk
|
||||
mode: trunk
|
||||
trunk_vlans:
|
||||
- blue
|
||||
- green
|
||||
native_vlan: 100
|
||||
active: True
|
||||
state: present
|
||||
|
||||
- name: Configure interface in access and trunk mode using aggregate
|
||||
junos_l2_interface:
|
||||
aggregate:
|
||||
- name: ge-0/0/1
|
||||
description: test-interface-access
|
||||
mode: access
|
||||
access_vlan: red
|
||||
- name: ge-0/0/2
|
||||
description: test-interface-trunk
|
||||
mode: trunk
|
||||
trunk_vlans:
|
||||
- blue
|
||||
- green
|
||||
native_vlan: 100
|
||||
active: True
|
||||
state: present
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
diff:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit interfaces]
|
||||
+ ge-0/0/1 {
|
||||
+ description "l2 interface configured by Ansible";
|
||||
+ unit 0 {
|
||||
+ family ethernet-switching {
|
||||
+ interface-mode access;
|
||||
+ vlan {
|
||||
+ members red;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
"""
|
||||
import collections
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.common.utils import remove_default_spec
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, tostring
|
||||
from ansible.module_utils.network.junos.junos import load_config, map_params_to_obj, map_obj_to_ele
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config, to_param_list
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def validate_vlan_id(value, module):
|
||||
if value and not 0 <= value <= 4094:
|
||||
module.fail_json(msg='vlan_id must be between 1 and 4094')
|
||||
|
||||
|
||||
def validate_param_values(module, obj, param=None):
|
||||
if not param:
|
||||
param = module.params
|
||||
for key in obj:
|
||||
# validate the param value (if validator func exists)
|
||||
validator = globals().get('validate_%s' % key)
|
||||
if callable(validator):
|
||||
validator(param.get(key), module)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
element_spec = dict(
|
||||
name=dict(),
|
||||
mode=dict(choices=['access', 'trunk']),
|
||||
access_vlan=dict(),
|
||||
native_vlan=dict(type='int'),
|
||||
trunk_vlans=dict(type='list'),
|
||||
unit=dict(default=0, type='int'),
|
||||
filter_input=dict(),
|
||||
filter_output=dict(),
|
||||
description=dict(),
|
||||
enhanced_layer=dict(default=True, type='bool'),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
active=dict(default=True, type='bool')
|
||||
)
|
||||
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
|
||||
required_one_of = [['name', 'aggregate']]
|
||||
mutually_exclusive = [['name', 'aggregate'],
|
||||
['access_vlan', 'trunk_vlans'],
|
||||
['access_vlan', 'native_vlan']]
|
||||
|
||||
required_if = [('mode', 'access', ('access_vlan',)),
|
||||
('mode', 'trunk', ('trunk_vlans',))]
|
||||
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict', options=aggregate_spec, mutually_exclusive=mutually_exclusive, required_if=required_if),
|
||||
)
|
||||
|
||||
argument_spec.update(element_spec)
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
required_one_of=required_one_of,
|
||||
required_if=required_if)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False}
|
||||
|
||||
if warnings:
|
||||
result['warnings'] = warnings
|
||||
|
||||
top = 'interfaces/interface'
|
||||
|
||||
param_to_xpath_map = collections.OrderedDict()
|
||||
param_to_xpath_map.update([
|
||||
('name', {'xpath': 'name', 'is_key': True}),
|
||||
('unit', {'xpath': 'name', 'top': 'unit', 'is_key': True}),
|
||||
('mode', {'xpath': 'interface-mode', 'top': 'unit/family/ethernet-switching'}),
|
||||
('access_vlan', {'xpath': 'members', 'top': 'unit/family/ethernet-switching/vlan'}),
|
||||
('trunk_vlans', {'xpath': 'members', 'top': 'unit/family/ethernet-switching/vlan'}),
|
||||
('filter_input', {'xpath': 'input', 'top': 'unit/family/ethernet-switching/filter'}),
|
||||
('filter_output', {'xpath': 'output', 'top': 'unit/family/ethernet-switching/filter'}),
|
||||
('native_vlan', {'xpath': 'native-vlan-id'}),
|
||||
('description', 'description')
|
||||
])
|
||||
|
||||
params = to_param_list(module)
|
||||
|
||||
requests = list()
|
||||
for param in params:
|
||||
# if key doesn't exist in the item, get it from module.params
|
||||
for key in param:
|
||||
if param.get(key) is None:
|
||||
param[key] = module.params[key]
|
||||
|
||||
item = param.copy()
|
||||
|
||||
validate_param_values(module, param_to_xpath_map, param=item)
|
||||
|
||||
param_to_xpath_map['mode']['xpath'] = \
|
||||
'interface-mode' if param['enhanced_layer'] else 'port-mode'
|
||||
|
||||
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):
|
||||
for req in requests:
|
||||
diff = load_config(module, tostring(req), warnings, action='replace')
|
||||
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,229 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_l3_interface
|
||||
version_added: "2.4"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
short_description: Manage L3 interfaces on Juniper JUNOS network devices
|
||||
description:
|
||||
- This module provides declarative management of L3 interfaces
|
||||
on Juniper JUNOS network devices.
|
||||
deprecated:
|
||||
removed_in: "2.13"
|
||||
why: Updated modules released with more functionality
|
||||
alternative: Use M(junos_l3_interfaces) instead.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the L3 interface.
|
||||
ipv4:
|
||||
description:
|
||||
- IPv4 of the L3 interface.
|
||||
ipv6:
|
||||
description:
|
||||
- IPv6 of the L3 interface.
|
||||
unit:
|
||||
description:
|
||||
- Logical interface number.
|
||||
default: 0
|
||||
filter_input:
|
||||
description:
|
||||
- The name of input filter.
|
||||
version_added: "2.8"
|
||||
filter_output:
|
||||
description:
|
||||
- The name of output filter.
|
||||
version_added: "2.8"
|
||||
filter6_input:
|
||||
description:
|
||||
- The name of input filter for ipv6.
|
||||
version_added: "2.8"
|
||||
filter6_output:
|
||||
description:
|
||||
- The name of output filter for ipv6.
|
||||
version_added: "2.8"
|
||||
aggregate:
|
||||
description: List of L3 interfaces definitions
|
||||
state:
|
||||
description:
|
||||
- State of the L3 interface configuration.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
default: True
|
||||
type: bool
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Set ge-0/0/1 IPv4 address
|
||||
junos_l3_interface:
|
||||
name: ge-0/0/1
|
||||
ipv4: 192.168.0.1
|
||||
|
||||
- name: Remove ge-0/0/1 IPv4 address
|
||||
junos_l3_interface:
|
||||
name: ge-0/0/1
|
||||
state: absent
|
||||
|
||||
- name: Set ipv4 address using aggregate
|
||||
junos_l3_interface:
|
||||
aggregate:
|
||||
- name: ge-0/0/1
|
||||
ipv4: 192.0.2.1
|
||||
- name: ge-0/0/2
|
||||
ipv4: 192.0.2.2
|
||||
ipv6: fd5d:12c9:2201:2::2
|
||||
|
||||
- name: Delete ipv4 address using aggregate
|
||||
junos_l3_interface:
|
||||
aggregate:
|
||||
- name: ge-0/0/1
|
||||
ipv4: 192.0.2.1
|
||||
- name: ge-0/0/2
|
||||
ipv4: 192.0.2.2
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
diff:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit interfaces ge-0/0/1 unit 0 family inet]
|
||||
+ address 192.0.2.1/32;
|
||||
[edit interfaces ge-0/0/1 unit 0 family inet6]
|
||||
+ address fd5d:12c9:2201:1::1/128;
|
||||
"""
|
||||
import collections
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.common.utils import remove_default_spec
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, tostring
|
||||
from ansible.module_utils.network.junos.junos import load_config, map_params_to_obj, map_obj_to_ele
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config, to_param_list
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
element_spec = dict(
|
||||
name=dict(),
|
||||
ipv4=dict(),
|
||||
ipv6=dict(),
|
||||
filter_input=dict(),
|
||||
filter_output=dict(),
|
||||
filter6_input=dict(),
|
||||
filter6_output=dict(),
|
||||
unit=dict(default=0, type='int'),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
active=dict(default=True, type='bool')
|
||||
)
|
||||
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
|
||||
)
|
||||
|
||||
argument_spec.update(element_spec)
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
required_one_of = [['name', 'aggregate']]
|
||||
mutually_exclusive = [['name', 'aggregate']]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
required_one_of=required_one_of)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False}
|
||||
|
||||
if warnings:
|
||||
result['warnings'] = warnings
|
||||
|
||||
top = 'interfaces/interface'
|
||||
|
||||
param_to_xpath_map = collections.OrderedDict()
|
||||
param_to_xpath_map.update([
|
||||
('name', {'xpath': 'name', 'parent_attrib': False, 'is_key': True}),
|
||||
('unit', {'xpath': 'name', 'top': 'unit', 'parent_attrib': False, 'is_key': True}),
|
||||
('ipv4', {'xpath': 'inet/address/name', 'top': 'unit/family', 'is_key': True}),
|
||||
('ipv6', {'xpath': 'inet6/address/name', 'top': 'unit/family', 'is_key': True}),
|
||||
('filter_input', {'xpath': 'inet/filter/input', 'top': 'unit/family'}),
|
||||
('filter_output', {'xpath': 'inet/filter/output', 'top': 'unit/family'}),
|
||||
('filter6_input', {'xpath': 'inet6/filter/input', 'top': 'unit/family'}),
|
||||
('filter6_output', {'xpath': 'inet6/filter/output', 'top': 'unit/family'}),
|
||||
])
|
||||
|
||||
params = to_param_list(module)
|
||||
|
||||
requests = list()
|
||||
for param in params:
|
||||
# if key doesn't exist in the item, get it from module.params
|
||||
for key in param:
|
||||
if param.get(key) is None:
|
||||
param[key] = module.params[key]
|
||||
|
||||
item = param.copy()
|
||||
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):
|
||||
for req in requests:
|
||||
diff = load_config(module, tostring(req), warnings, action='merge')
|
||||
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,347 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_linkagg
|
||||
version_added: "2.4"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
short_description: Manage link aggregation groups on Juniper JUNOS network devices
|
||||
description:
|
||||
- This module provides declarative management of link aggregation groups
|
||||
on Juniper JUNOS network devices.
|
||||
deprecated:
|
||||
removed_in: "2.13"
|
||||
why: Updated modules released with more functionality
|
||||
alternative: Use M(junos_lag_interfaces) instead.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the link aggregation group.
|
||||
required: true
|
||||
mode:
|
||||
description:
|
||||
- Mode of the link aggregation group. A value of C(on) will enable LACP in C(passive) mode.
|
||||
C(active) configures the link to actively information about the state of the link,
|
||||
or it can be configured in C(passive) mode ie. send link state information only when
|
||||
received them from another link. A value of C(off) will disable LACP.
|
||||
default: off
|
||||
choices: ['on', 'off', 'active', 'passive']
|
||||
members:
|
||||
description:
|
||||
- List of members interfaces of the link aggregation group. The value can be
|
||||
single interface or list of interfaces.
|
||||
required: true
|
||||
min_links:
|
||||
description:
|
||||
- Minimum members that should be up
|
||||
before bringing up the link aggregation group.
|
||||
device_count:
|
||||
description:
|
||||
- Number of aggregated ethernet devices that can be configured.
|
||||
Acceptable integer value is between 1 and 128.
|
||||
description:
|
||||
description:
|
||||
- Description of Interface.
|
||||
aggregate:
|
||||
description: List of link aggregation definitions.
|
||||
state:
|
||||
description:
|
||||
- State of the link aggregation group.
|
||||
default: present
|
||||
choices: ['present', 'absent', 'up', 'down']
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
default: True
|
||||
type: bool
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure link aggregation
|
||||
junos_linkagg:
|
||||
name: ae11
|
||||
members:
|
||||
- ge-0/0/5
|
||||
- ge-0/0/6
|
||||
- ge-0/0/7
|
||||
lacp: active
|
||||
device_count: 4
|
||||
state: present
|
||||
|
||||
- name: delete link aggregation
|
||||
junos_linkagg:
|
||||
name: ae11
|
||||
members:
|
||||
- ge-0/0/5
|
||||
- ge-0/0/6
|
||||
- ge-0/0/7
|
||||
lacp: active
|
||||
device_count: 4
|
||||
state: delete
|
||||
|
||||
- name: deactivate link aggregation
|
||||
junos_linkagg:
|
||||
name: ae11
|
||||
members:
|
||||
- ge-0/0/5
|
||||
- ge-0/0/6
|
||||
- ge-0/0/7
|
||||
lacp: active
|
||||
device_count: 4
|
||||
state: present
|
||||
active: False
|
||||
|
||||
- name: Activate link aggregation
|
||||
junos_linkagg:
|
||||
name: ae11
|
||||
members:
|
||||
- ge-0/0/5
|
||||
- ge-0/0/6
|
||||
- ge-0/0/7
|
||||
lacp: active
|
||||
device_count: 4
|
||||
state: present
|
||||
active: True
|
||||
|
||||
- name: Disable link aggregation
|
||||
junos_linkagg:
|
||||
name: ae11
|
||||
state: down
|
||||
|
||||
- name: Enable link aggregation
|
||||
junos_linkagg:
|
||||
name: ae11
|
||||
state: up
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
diff:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit interfaces]
|
||||
+ ge-0/0/6 {
|
||||
+ ether-options {
|
||||
+ 802.3ad ae0;
|
||||
+ }
|
||||
+ }
|
||||
[edit interfaces ge-0/0/7]
|
||||
+ ether-options {
|
||||
+ 802.3ad ae0;
|
||||
+ }
|
||||
[edit interfaces]
|
||||
+ ae0 {
|
||||
+ description "configured by junos_linkagg";
|
||||
+ aggregated-ether-options {
|
||||
+ lacp {
|
||||
+ active;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
"""
|
||||
import collections
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.common.utils import remove_default_spec
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, tostring
|
||||
from ansible.module_utils.network.junos.junos import load_config, map_params_to_obj, map_obj_to_ele, to_param_list
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config, get_configuration
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def validate_device_count(value, module):
|
||||
if value and not 1 <= value <= 128:
|
||||
module.fail_json(msg='device_count must be between 1 and 128')
|
||||
|
||||
|
||||
def validate_min_links(value, module):
|
||||
if value and not 1 <= value <= 8:
|
||||
module.fail_json(msg='min_links must be between 1 and 8')
|
||||
|
||||
|
||||
def validate_param_values(module, obj, item):
|
||||
for key in obj:
|
||||
# validate the param value (if validator func exists)
|
||||
validator = globals().get('validate_%s' % key)
|
||||
if callable(validator):
|
||||
validator(item.get(key), module)
|
||||
|
||||
|
||||
def configure_lag_params(module, requests, item):
|
||||
top = 'interfaces/interface'
|
||||
param_lag_to_xpath_map = collections.OrderedDict()
|
||||
param_lag_to_xpath_map.update([
|
||||
('name', {'xpath': 'name', 'is_key': True}),
|
||||
('description', 'description'),
|
||||
('min_links', {'xpath': 'minimum-links', 'top': 'aggregated-ether-options'}),
|
||||
('disable', {'xpath': 'disable', 'tag_only': True}),
|
||||
('mode', {'xpath': item['mode'], 'tag_only': True, 'top': 'aggregated-ether-options/lacp'}),
|
||||
])
|
||||
|
||||
validate_param_values(module, param_lag_to_xpath_map, item)
|
||||
|
||||
want = map_params_to_obj(module, param_lag_to_xpath_map, param=item)
|
||||
ele = map_obj_to_ele(module, want, top, param=item)
|
||||
requests.append(ele)
|
||||
|
||||
if item['device_count']:
|
||||
top = 'chassis/aggregated-devices/ethernet'
|
||||
device_count_to_xpath_map = {'device_count': {'xpath': 'device-count', 'leaf_only': True}}
|
||||
|
||||
validate_param_values(module, device_count_to_xpath_map, item)
|
||||
|
||||
want = map_params_to_obj(module, device_count_to_xpath_map, param=item)
|
||||
ele = map_obj_to_ele(module, want, top, param=item)
|
||||
requests.append(ele)
|
||||
|
||||
|
||||
def configure_member_params(module, requests, item):
|
||||
top = 'interfaces/interface'
|
||||
members = item['members']
|
||||
|
||||
if members:
|
||||
member_to_xpath_map = collections.OrderedDict()
|
||||
member_to_xpath_map.update([
|
||||
('name', {'xpath': 'name', 'is_key': True, 'parent_attrib': False}),
|
||||
('bundle', {'xpath': 'bundle', 'leaf_only': True, 'top': 'ether-options/ieee-802.3ad', 'is_key': True}),
|
||||
])
|
||||
|
||||
# link aggregation bundle assigned to member
|
||||
item['bundle'] = item['name']
|
||||
|
||||
for member in members:
|
||||
|
||||
if item['state'] == 'absent':
|
||||
# if link aggregate bundle is not assigned to member, trying to
|
||||
# delete it results in rpc-reply error, hence if is not assigned
|
||||
# skip deleting it and continue to next member.
|
||||
resp = get_configuration(module)
|
||||
bundle = resp.xpath("configuration/interfaces/interface[name='%s']/ether-options/"
|
||||
"ieee-802.3ad[bundle='%s']" % (member, item['bundle']))
|
||||
if not bundle:
|
||||
continue
|
||||
# Name of member to be assigned to link aggregation bundle
|
||||
item['name'] = member
|
||||
|
||||
validate_param_values(module, member_to_xpath_map, item)
|
||||
|
||||
want = map_params_to_obj(module, member_to_xpath_map, param=item)
|
||||
ele = map_obj_to_ele(module, want, top, param=item)
|
||||
requests.append(ele)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
element_spec = dict(
|
||||
name=dict(),
|
||||
mode=dict(default='on', choices=['on', 'off', 'active', 'passive']),
|
||||
members=dict(type='list'),
|
||||
min_links=dict(type='int'),
|
||||
device_count=dict(type='int'),
|
||||
description=dict(),
|
||||
state=dict(default='present', choices=['present', 'absent', 'up', 'down']),
|
||||
active=dict(default=True, type='bool')
|
||||
)
|
||||
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
|
||||
)
|
||||
|
||||
argument_spec.update(element_spec)
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
required_one_of = [['name', 'aggregate']]
|
||||
mutually_exclusive = [['name', 'aggregate']]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False}
|
||||
|
||||
if warnings:
|
||||
result['warnings'] = warnings
|
||||
|
||||
params = to_param_list(module)
|
||||
requests = list()
|
||||
for param in params:
|
||||
# if key doesn't exist in the item, get it from module.params
|
||||
for key in param:
|
||||
if param.get(key) is None:
|
||||
param[key] = module.params[key]
|
||||
|
||||
item = param.copy()
|
||||
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
|
||||
|
||||
mode = item.get('mode')
|
||||
if mode == 'off':
|
||||
item['mode'] = ''
|
||||
elif mode == 'on':
|
||||
item['mode'] = 'passive'
|
||||
|
||||
configure_lag_params(module, requests, item)
|
||||
configure_member_params(module, requests, item)
|
||||
|
||||
diff = None
|
||||
with locked_config(module):
|
||||
for req in requests:
|
||||
diff = load_config(module, tostring(req), warnings, action='merge')
|
||||
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,199 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_lldp
|
||||
version_added: "2.4"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
short_description: Manage LLDP configuration on Juniper JUNOS network devices
|
||||
description:
|
||||
- This module provides declarative management of LLDP service
|
||||
on Juniper JUNOS network devices.
|
||||
deprecated:
|
||||
removed_in: "2.13"
|
||||
why: Updated modules released with more functionality
|
||||
alternative: Use M(junos_lldp_global) instead.
|
||||
options:
|
||||
interval:
|
||||
description:
|
||||
- Frequency at which LLDP advertisements are sent (in seconds).
|
||||
transmit_delay:
|
||||
description:
|
||||
- Specify the number of seconds the device waits before sending
|
||||
advertisements to neighbors after a change is made in local system.
|
||||
hold_multiplier:
|
||||
description:
|
||||
- Specify the number of seconds that LLDP information is held before it is
|
||||
discarded. The multiplier value is used in combination with the
|
||||
C(interval) value.
|
||||
state:
|
||||
description:
|
||||
- Value of C(present) ensures given LLDP configuration
|
||||
is present on device and LLDP is enabled, for value of C(absent)
|
||||
LLDP configuration is deleted and LLDP is in disabled state.
|
||||
Value C(enabled) ensures LLDP protocol is enabled and LLDP configuration
|
||||
if any is configured on remote device, for value of C(disabled) it ensures
|
||||
LLDP protocol is disabled any LLDP configuration if any is still present.
|
||||
default: present
|
||||
choices: ['present', 'absent', 'enabled', 'disabled']
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
default: True
|
||||
type: bool
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Enable LLDP service
|
||||
junos_lldp:
|
||||
state: enabled
|
||||
|
||||
- name: Disable LLDP service
|
||||
junos_lldp:
|
||||
state: disabled
|
||||
|
||||
- name: Set LLDP parameters
|
||||
junos_lldp:
|
||||
interval: 10
|
||||
hold_multiplier: 5
|
||||
transmit_delay: 30
|
||||
state: present
|
||||
|
||||
- name: Delete LLDP parameters
|
||||
junos_lldp:
|
||||
interval: 10
|
||||
hold_multiplier: 5
|
||||
transmit_delay: 30
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
diff.prepared:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit]
|
||||
+ protocols {
|
||||
+ lldp {
|
||||
+ disable;
|
||||
+ }
|
||||
+ }
|
||||
"""
|
||||
import collections
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, tostring
|
||||
from ansible.module_utils.network.junos.junos import load_config, map_params_to_obj, map_obj_to_ele
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def validate_interval(value, module):
|
||||
if not 5 <= value <= 32768:
|
||||
module.fail_json(msg='interval must be between 5 and 32768')
|
||||
|
||||
|
||||
def validate_hold_multiplier(value, module):
|
||||
if not 5 <= value <= 32768:
|
||||
module.fail_json(msg='hold_multiplier must be between 2 and 10')
|
||||
|
||||
|
||||
def validate_transmit_delay(value, module):
|
||||
if not 1 <= value <= 8192:
|
||||
module.fail_json(msg='transmit_delay must be between 2 and 10')
|
||||
|
||||
|
||||
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():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
interval=dict(type='int'),
|
||||
transmit_delay=dict(type='int'),
|
||||
hold_multiplier=dict(type='int'),
|
||||
state=dict(default='present', choices=['present', 'absent', 'enabled', 'disabled']),
|
||||
active=dict(default=True, type='bool')
|
||||
)
|
||||
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False}
|
||||
|
||||
if warnings:
|
||||
result['warnings'] = warnings
|
||||
|
||||
top = 'protocols/lldp'
|
||||
|
||||
param_to_xpath_map = collections.OrderedDict()
|
||||
param_to_xpath_map.update([
|
||||
('interval', {'xpath': 'advertisement-interval', 'leaf_only': True}),
|
||||
('transmit_delay', {'xpath': 'transmit-delay', 'leaf_only': True}),
|
||||
('hold_multiplier', {'xpath': 'hold-multiplier', 'leaf_only': True}),
|
||||
('disable', {'xpath': 'disable', 'tag_only': True, 'is_key': True})
|
||||
])
|
||||
|
||||
item = module.params.copy()
|
||||
state = item.get('state')
|
||||
|
||||
item['disable'] = True if state in ('disabled', 'absent') else False
|
||||
|
||||
if state in ('enabled', 'disabled'):
|
||||
item['state'] = 'present'
|
||||
|
||||
want = map_params_to_obj(module, param_to_xpath_map, param=item)
|
||||
ele = map_obj_to_ele(module, want, top, param=item)
|
||||
|
||||
with locked_config(module):
|
||||
diff = load_config(module, tostring(ele), warnings, action='merge')
|
||||
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,166 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_lldp_interface
|
||||
version_added: "2.4"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
short_description: Manage LLDP interfaces configuration on Juniper JUNOS network devices
|
||||
description:
|
||||
- This module provides declarative management of LLDP interfaces
|
||||
configuration on Juniper JUNOS network devices.
|
||||
deprecated:
|
||||
removed_in: "2.13"
|
||||
why: Updated modules released with more functionality
|
||||
alternative: Use M(junos_lldp_interfaces) instead.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the interface LLDP should be configured on.
|
||||
state:
|
||||
description:
|
||||
- Value of C(present) ensures given LLDP configured on given I(interfaces)
|
||||
and is enabled, for value of C(absent) LLDP configuration on given I(interfaces) deleted.
|
||||
Value C(enabled) ensures LLDP protocol is enabled on given I(interfaces) and
|
||||
for value of C(disabled) it ensures LLDP is disabled on given I(interfaces).
|
||||
default: present
|
||||
choices: ['present', 'absent', 'enabled', 'disabled']
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
default: True
|
||||
type: bool
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Configure LLDP on specific interfaces
|
||||
junos_lldp_interface:
|
||||
name: ge-0/0/5
|
||||
state: present
|
||||
|
||||
- name: Disable LLDP on specific interfaces
|
||||
junos_lldp_interface:
|
||||
name: ge-0/0/5
|
||||
state: disabled
|
||||
|
||||
- name: Enable LLDP on specific interfaces
|
||||
junos_lldp_interface:
|
||||
name: ge-0/0/5
|
||||
state: enabled
|
||||
|
||||
- name: Delete LLDP configuration on specific interfaces
|
||||
junos_lldp_interface:
|
||||
name: ge-0/0/5
|
||||
state: present
|
||||
|
||||
- name: Deactivate LLDP on specific interfaces
|
||||
junos_lldp_interface:
|
||||
name: ge-0/0/5
|
||||
state: present
|
||||
active: False
|
||||
|
||||
- name: Activate LLDP on specific interfaces
|
||||
junos_lldp_interface:
|
||||
name: ge-0/0/5
|
||||
state: present
|
||||
active: True
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
diff.prepared:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit protocols lldp]
|
||||
+ interface ge-0/0/5;
|
||||
"""
|
||||
import collections
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec
|
||||
from ansible.module_utils.network.junos.junos import load_config, map_params_to_obj, map_obj_to_ele, tostring
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
name=dict(),
|
||||
state=dict(default='present', choices=['present', 'absent', 'enabled', 'disabled']),
|
||||
active=dict(default=True, type='bool')
|
||||
)
|
||||
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False}
|
||||
|
||||
if warnings:
|
||||
result['warnings'] = warnings
|
||||
|
||||
top = 'protocols/lldp/interface'
|
||||
|
||||
param_to_xpath_map = collections.OrderedDict()
|
||||
param_to_xpath_map.update([
|
||||
('name', {'xpath': 'name', 'is_key': True}),
|
||||
('disable', {'xpath': 'disable', 'tag_only': True})
|
||||
])
|
||||
|
||||
item = module.params.copy()
|
||||
state = item.get('state')
|
||||
item['disable'] = True if state in ('disabled', 'absent') else False
|
||||
|
||||
if state in ('enabled', 'disabled'):
|
||||
item['state'] = 'present'
|
||||
|
||||
want = map_params_to_obj(module, param_to_xpath_map, param=item)
|
||||
ele = map_obj_to_ele(module, want, top, param=item)
|
||||
|
||||
with locked_config(module):
|
||||
diff = load_config(module, tostring(ele), warnings, action='merge')
|
||||
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,240 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_static_route
|
||||
version_added: "2.4"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
short_description: Manage static IP routes on Juniper JUNOS network devices
|
||||
description:
|
||||
- This module provides declarative management of static
|
||||
IP routes on Juniper JUNOS network devices.
|
||||
deprecated:
|
||||
removed_in: "2.13"
|
||||
why: Updated modules released with more functionality
|
||||
alternative: Use M(junos_static_routes) instead.
|
||||
options:
|
||||
address:
|
||||
description:
|
||||
- Network address with prefix of the static route.
|
||||
required: true
|
||||
aliases: ['prefix']
|
||||
next_hop:
|
||||
description:
|
||||
- Next hop IP of the static route.
|
||||
required: true
|
||||
qualified_next_hop:
|
||||
description:
|
||||
- Qualified next hop IP of the static route. Qualified next hops allow
|
||||
to associate preference with a particular next-hop address.
|
||||
preference:
|
||||
description:
|
||||
- Global admin preference of the static route.
|
||||
aliases: ['admin_distance']
|
||||
qualified_preference:
|
||||
description:
|
||||
- Assign preference for qualified next hop.
|
||||
aggregate:
|
||||
description: List of static route definitions
|
||||
state:
|
||||
description:
|
||||
- State of the static route configuration.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
default: True
|
||||
type: bool
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure static route
|
||||
junos_static_route:
|
||||
address: 192.168.2.0/24
|
||||
next_hop: 10.0.0.1
|
||||
preference: 10
|
||||
qualified_next_hop: 10.0.0.2
|
||||
qualified_preference: 3
|
||||
state: present
|
||||
|
||||
- name: delete static route
|
||||
junos_static_route:
|
||||
address: 192.168.2.0/24
|
||||
state: absent
|
||||
|
||||
- name: deactivate static route configuration
|
||||
junos_static_route:
|
||||
address: 192.168.2.0/24
|
||||
next_hop: 10.0.0.1
|
||||
preference: 10
|
||||
qualified_next_hop: 10.0.0.2
|
||||
qualified_preference: 3
|
||||
state: present
|
||||
active: False
|
||||
|
||||
- name: activate static route configuration
|
||||
junos_static_route:
|
||||
address: 192.168.2.0/24
|
||||
next_hop: 10.0.0.1
|
||||
preference: 10
|
||||
qualified_next_hop: 10.0.0.2
|
||||
qualified_preference: 3
|
||||
state: present
|
||||
active: True
|
||||
|
||||
- name: Configure static route using aggregate
|
||||
junos_static_route:
|
||||
aggregate:
|
||||
- { address: 4.4.4.0/24, next_hop: 3.3.3.3, qualified_next_hop: 5.5.5.5, qualified_preference: 30 }
|
||||
- { address: 5.5.5.0/24, next_hop: 6.6.6.6, qualified_next_hop: 7.7.7.7, qualified_preference: 12 }
|
||||
preference: 10
|
||||
|
||||
- name: Delete static route using aggregate
|
||||
junos_static_route:
|
||||
aggregate:
|
||||
- address: 4.4.4.0/24
|
||||
- address: 5.5.5.0/24
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
diff.prepared:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit routing-options static]
|
||||
route 2.2.2.0/24 { ... }
|
||||
+ route 4.4.4.0/24 {
|
||||
next-hop 3.3.3.3;
|
||||
qualified-next-hop 5.5.5.5 {
|
||||
+ preference 30;
|
||||
}
|
||||
+ preference 10;
|
||||
+ }
|
||||
"""
|
||||
import collections
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.common.utils import remove_default_spec
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, tostring
|
||||
from ansible.module_utils.network.junos.junos import load_config, map_params_to_obj, map_obj_to_ele, to_param_list
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
element_spec = dict(
|
||||
address=dict(aliases=['prefix']),
|
||||
next_hop=dict(),
|
||||
preference=dict(type='int', aliases=['admin_distance']),
|
||||
qualified_next_hop=dict(type='str'),
|
||||
qualified_preference=dict(type='int'),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
active=dict(default=True, type='bool')
|
||||
)
|
||||
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['address'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
|
||||
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)
|
||||
|
||||
required_one_of = [['aggregate', 'address']]
|
||||
mutually_exclusive = [['aggregate', 'address']]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False}
|
||||
|
||||
if warnings:
|
||||
result['warnings'] = warnings
|
||||
|
||||
top = 'routing-options/static/route'
|
||||
|
||||
param_to_xpath_map = collections.OrderedDict()
|
||||
param_to_xpath_map.update([
|
||||
('address', {'xpath': 'name', 'is_key': True}),
|
||||
('next_hop', 'next-hop'),
|
||||
('preference', 'preference/metric-value'),
|
||||
('qualified_next_hop', {'xpath': 'name', 'top': 'qualified-next-hop'}),
|
||||
('qualified_preference', {'xpath': 'preference', 'top': 'qualified-next-hop'})
|
||||
])
|
||||
|
||||
params = to_param_list(module)
|
||||
requests = list()
|
||||
|
||||
for param in params:
|
||||
# if key doesn't exist in the item, get it from module.params
|
||||
for key in param:
|
||||
if param.get(key) is None:
|
||||
param[key] = module.params[key]
|
||||
|
||||
item = param.copy()
|
||||
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, param=item)
|
||||
requests.append(map_obj_to_ele(module, want, top, param=item))
|
||||
|
||||
with locked_config(module):
|
||||
for req in requests:
|
||||
diff = load_config(module, tostring(req), warnings, action='merge')
|
||||
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,251 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['deprecated'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_vlan
|
||||
version_added: "2.4"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
short_description: Manage VLANs on Juniper JUNOS network devices
|
||||
description:
|
||||
- This module provides declarative management of VLANs
|
||||
on Juniper JUNOS network devices.
|
||||
deprecated:
|
||||
removed_in: "2.13"
|
||||
why: Updated modules released with more functionality
|
||||
alternative: Use M(junos_vlans) instead.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the VLAN.
|
||||
required: true
|
||||
vlan_id:
|
||||
description:
|
||||
- ID of the VLAN. Range 1-4094.
|
||||
required: true
|
||||
l3_interface:
|
||||
description:
|
||||
- Name of logical layer 3 interface.
|
||||
version_added: "2.7"
|
||||
filter_input:
|
||||
description:
|
||||
- The name of input filter.
|
||||
version_added: "2.8"
|
||||
filter_output:
|
||||
description:
|
||||
- The name of output filter.
|
||||
version_added: "2.8"
|
||||
description:
|
||||
description:
|
||||
- Text description of VLANs.
|
||||
interfaces:
|
||||
description:
|
||||
- List of interfaces to check the VLAN has been
|
||||
configured correctly.
|
||||
aggregate:
|
||||
description: List of VLANs definitions.
|
||||
state:
|
||||
description:
|
||||
- State of the VLAN configuration.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
default: True
|
||||
type: bool
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure VLAN ID and name
|
||||
junos_vlan:
|
||||
name: test
|
||||
vlan_id: 20
|
||||
|
||||
- name: Link to logical layer 3 interface
|
||||
junos_vlan:
|
||||
name: test
|
||||
vlan_id: 20
|
||||
l3-interface: vlan.20
|
||||
|
||||
- name: remove VLAN configuration
|
||||
junos_vlan:
|
||||
name: test
|
||||
state: absent
|
||||
|
||||
- name: deactive VLAN configuration
|
||||
junos_vlan:
|
||||
name: test
|
||||
state: present
|
||||
active: False
|
||||
|
||||
- name: activate VLAN configuration
|
||||
junos_vlan:
|
||||
name: test
|
||||
state: present
|
||||
active: True
|
||||
|
||||
- name: Create vlan configuration using aggregate
|
||||
junos_vlan:
|
||||
aggregate:
|
||||
- { vlan_id: 159, name: test_vlan_1, description: test vlan-1 }
|
||||
- { vlan_id: 160, name: test_vlan_2, description: test vlan-2 }
|
||||
|
||||
- name: Delete vlan configuration using aggregate
|
||||
junos_vlan:
|
||||
aggregate:
|
||||
- { vlan_id: 159, name: test_vlan_1 }
|
||||
- { vlan_id: 160, name: test_vlan_2 }
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
diff.prepared:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit vlans]
|
||||
+ test-vlan-1 {
|
||||
+ vlan-id 60;
|
||||
+ }
|
||||
"""
|
||||
import collections
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.common.utils import remove_default_spec
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, tostring
|
||||
from ansible.module_utils.network.junos.junos import load_config, map_params_to_obj, map_obj_to_ele, to_param_list
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def validate_vlan_id(value, module):
|
||||
if value and not 1 <= value <= 4094:
|
||||
module.fail_json(msg='vlan_id must be between 1 and 4094')
|
||||
|
||||
|
||||
def validate_param_values(module, obj, param=None):
|
||||
if not param:
|
||||
param = module.params
|
||||
for key in obj:
|
||||
# validate the param value (if validator func exists)
|
||||
validator = globals().get('validate_%s' % key)
|
||||
if callable(validator):
|
||||
validator(param.get(key), module)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
element_spec = dict(
|
||||
name=dict(),
|
||||
vlan_id=dict(type='int'),
|
||||
description=dict(),
|
||||
interfaces=dict(),
|
||||
l3_interface=dict(),
|
||||
filter_input=dict(),
|
||||
filter_output=dict(),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
active=dict(default=True, type='bool')
|
||||
)
|
||||
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict', options=aggregate_spec)
|
||||
)
|
||||
|
||||
argument_spec.update(element_spec)
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
required_one_of = [['aggregate', 'name']]
|
||||
mutually_exclusive = [['aggregate', 'name']]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False}
|
||||
|
||||
if warnings:
|
||||
result['warnings'] = warnings
|
||||
|
||||
top = 'vlans/vlan'
|
||||
|
||||
param_to_xpath_map = collections.OrderedDict()
|
||||
param_to_xpath_map.update([
|
||||
('name', {'xpath': 'name', 'is_key': True}),
|
||||
('vlan_id', 'vlan-id'),
|
||||
('l3_interface', 'l3-interface'),
|
||||
('filter_input', 'forwarding-options/filter/input'),
|
||||
('filter_output', 'forwarding-options/filter/output'),
|
||||
('description', 'description')
|
||||
])
|
||||
|
||||
params = to_param_list(module)
|
||||
requests = list()
|
||||
|
||||
for param in params:
|
||||
# if key doesn't exist in the item, get it from module.params
|
||||
for key in param:
|
||||
if param.get(key) is None:
|
||||
param[key] = module.params[key]
|
||||
|
||||
item = param.copy()
|
||||
|
||||
validate_param_values(module, param_to_xpath_map, param=item)
|
||||
|
||||
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):
|
||||
for req in requests:
|
||||
diff = load_config(module, tostring(req), warnings, action='merge')
|
||||
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,177 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_banner
|
||||
version_added: "2.4"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
short_description: Manage multiline banners on Juniper JUNOS devices
|
||||
description:
|
||||
- This will configure both login and motd banners on network devices.
|
||||
It allows playbooks to add or remote
|
||||
banner text from the active running configuration.
|
||||
options:
|
||||
banner:
|
||||
description:
|
||||
- Specifies which banner that should be
|
||||
configured on the remote device. Value C(login) indicates
|
||||
system login message prior to authenticating, C(motd) is login
|
||||
announcement after successful authentication.
|
||||
required: true
|
||||
choices: ['login', 'motd']
|
||||
text:
|
||||
description:
|
||||
- The banner text that should be
|
||||
present in the remote device running configuration. This argument
|
||||
accepts a multiline string, with no empty lines. Requires I(state=present).
|
||||
state:
|
||||
description:
|
||||
- Specifies whether or not the configuration is
|
||||
present in the current devices active running configuration.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
type: bool
|
||||
default: 'yes'
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure the login banner
|
||||
junos_banner:
|
||||
banner: login
|
||||
text: |
|
||||
this is my login banner
|
||||
that contains a multiline
|
||||
string
|
||||
state: present
|
||||
|
||||
- name: remove the motd banner
|
||||
junos_banner:
|
||||
banner: motd
|
||||
state: absent
|
||||
|
||||
- name: deactivate the motd banner
|
||||
junos_banner:
|
||||
banner: motd
|
||||
state: present
|
||||
active: False
|
||||
|
||||
- name: activate the motd banner
|
||||
junos_banner:
|
||||
banner: motd
|
||||
state: present
|
||||
active: True
|
||||
|
||||
- name: Configure banner from file
|
||||
junos_banner:
|
||||
banner: motd
|
||||
text: "{{ lookup('file', './config_partial/raw_banner.cfg') }}"
|
||||
state: present
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
diff.prepared:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit system login]
|
||||
+ message \"this is my login banner\";
|
||||
"""
|
||||
import collections
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, tostring
|
||||
from ansible.module_utils.network.junos.junos import load_config, map_params_to_obj, map_obj_to_ele
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config
|
||||
|
||||
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():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
banner=dict(required=True, choices=['login', 'motd']),
|
||||
text=dict(),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
active=dict(default=True, type='bool')
|
||||
)
|
||||
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
required_if = [('state', 'present', ('text',))]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False}
|
||||
|
||||
if warnings:
|
||||
result['warnings'] = warnings
|
||||
|
||||
top = 'system/login'
|
||||
|
||||
param_to_xpath_map = collections.OrderedDict()
|
||||
|
||||
param_to_xpath_map.update([
|
||||
('text', {'xpath': 'message' if module.params['banner'] == 'login' else 'announcement', 'leaf_only': True})
|
||||
])
|
||||
|
||||
validate_param_values(module, param_to_xpath_map)
|
||||
|
||||
want = map_params_to_obj(module, param_to_xpath_map)
|
||||
ele = map_obj_to_ele(module, want, top)
|
||||
|
||||
with locked_config(module):
|
||||
diff = load_config(module, tostring(ele), warnings, action='merge')
|
||||
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,443 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_command
|
||||
version_added: "2.1"
|
||||
author: "Peter Sprygada (@privateip)"
|
||||
short_description: Run arbitrary commands on an Juniper JUNOS device
|
||||
description:
|
||||
- Sends an arbitrary set of commands to an JUNOS node and returns the results
|
||||
read from the device. This module includes an
|
||||
argument that will cause the module to wait for a specific condition
|
||||
before returning or timing out if the condition is not met.
|
||||
extends_documentation_fragment: junos
|
||||
options:
|
||||
commands:
|
||||
description:
|
||||
- The commands to send to the remote junos device over the
|
||||
configured provider. The resulting output from the command
|
||||
is returned. If the I(wait_for) argument is provided, the
|
||||
module is not returned until the condition is satisfied or
|
||||
the number of I(retries) has been exceeded.
|
||||
rpcs:
|
||||
description:
|
||||
- The C(rpcs) argument accepts a list of RPCs to be executed
|
||||
over a netconf session and the results from the RPC execution
|
||||
is return to the playbook via the modules results dictionary.
|
||||
version_added: "2.3"
|
||||
wait_for:
|
||||
description:
|
||||
- Specifies what to evaluate from the output of the command
|
||||
and what conditionals to apply. This argument will cause
|
||||
the task to wait for a particular conditional to be true
|
||||
before moving forward. If the conditional is not true
|
||||
by the configured retries, the task fails. See examples.
|
||||
aliases: ['waitfor']
|
||||
version_added: "2.2"
|
||||
match:
|
||||
description:
|
||||
- The I(match) argument is used in conjunction with the
|
||||
I(wait_for) argument to specify the match policy. Valid
|
||||
values are C(all) or C(any). If the value is set to C(all)
|
||||
then all conditionals in the I(wait_for) must be satisfied. If
|
||||
the value is set to C(any) then only one of the values must be
|
||||
satisfied.
|
||||
default: all
|
||||
choices: ['any', 'all']
|
||||
version_added: "2.2"
|
||||
retries:
|
||||
description:
|
||||
- Specifies the number of retries a command should be tried
|
||||
before it is considered failed. The command is run on the
|
||||
target device every retry and evaluated against the I(wait_for)
|
||||
conditionals.
|
||||
default: 10
|
||||
interval:
|
||||
description:
|
||||
- Configures the interval in seconds to wait between retries
|
||||
of the command. If the command does not pass the specified
|
||||
conditional, the interval indicates how to long to wait before
|
||||
trying the command again.
|
||||
default: 1
|
||||
display:
|
||||
description:
|
||||
- Encoding scheme to use when serializing output from the device.
|
||||
This handles how to properly understand the output and apply the
|
||||
conditionals path to the result set. For I(rpcs) argument default
|
||||
display is C(xml) and for I(commands) argument default display
|
||||
is C(text). Value C(set) is applicable only for fetching configuration
|
||||
from device.
|
||||
default: depends on input argument I(rpcs) or I(commands)
|
||||
aliases: ['format', 'output']
|
||||
choices: ['text', 'json', 'xml', 'set']
|
||||
version_added: "2.3"
|
||||
requirements:
|
||||
- jxmlease
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(network_cli) connections and with C(local) connections for legacy playbooks.
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: run show version on remote devices
|
||||
junos_command:
|
||||
commands: show version
|
||||
|
||||
- name: run show version and check to see if output contains Juniper
|
||||
junos_command:
|
||||
commands: show version
|
||||
wait_for: result[0] contains Juniper
|
||||
|
||||
- name: run multiple commands on remote nodes
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces
|
||||
|
||||
- name: run multiple commands and evaluate the output
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces
|
||||
wait_for:
|
||||
- result[0] contains Juniper
|
||||
- result[1] contains Loopback0
|
||||
|
||||
- name: run commands and specify the output format
|
||||
junos_command:
|
||||
commands: show version
|
||||
display: json
|
||||
|
||||
- name: run rpc on the remote device
|
||||
junos_command:
|
||||
commands: show configuration
|
||||
display: set
|
||||
|
||||
- name: run rpc on the remote device
|
||||
junos_command:
|
||||
rpcs: get-software-information
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
stdout:
|
||||
description: The set of responses from the commands
|
||||
returned: always apart from low level errors (such as action plugin)
|
||||
type: list
|
||||
sample: ['...', '...']
|
||||
stdout_lines:
|
||||
description: The value of stdout split into a list
|
||||
returned: always apart from low level errors (such as action plugin)
|
||||
type: list
|
||||
sample: [['...', '...'], ['...'], ['...']]
|
||||
output:
|
||||
description: The set of transformed xml to json format from the commands responses
|
||||
returned: If the I(display) is in C(xml) format.
|
||||
type: list
|
||||
sample: ['...', '...']
|
||||
failed_conditions:
|
||||
description: The list of conditionals that have failed
|
||||
returned: failed
|
||||
type: list
|
||||
sample: ['...', '...']
|
||||
"""
|
||||
import re
|
||||
import shlex
|
||||
import time
|
||||
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.connection import ConnectionError
|
||||
from ansible.module_utils.network.common.netconf import exec_rpc
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, get_configuration, get_connection, get_capabilities, tostring
|
||||
from ansible.module_utils.network.common.parsing import Conditional, FailedConditionalError
|
||||
from ansible.module_utils.network.common.utils import to_lines
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
|
||||
try:
|
||||
from lxml.etree import Element, SubElement
|
||||
except ImportError:
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
|
||||
try:
|
||||
import jxmlease
|
||||
HAS_JXMLEASE = True
|
||||
except ImportError:
|
||||
HAS_JXMLEASE = False
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def rpc(module, items):
|
||||
|
||||
responses = list()
|
||||
for item in items:
|
||||
name = item['name']
|
||||
xattrs = item['xattrs']
|
||||
fetch_config = False
|
||||
|
||||
args = item.get('args')
|
||||
text = item.get('text')
|
||||
|
||||
name = str(name).replace('_', '-')
|
||||
|
||||
if all((module.check_mode, not name.startswith('get'))):
|
||||
module.fail_json(msg='invalid rpc for running in check_mode')
|
||||
|
||||
if name == 'command' and text.startswith('show configuration') or name == 'get-configuration':
|
||||
fetch_config = True
|
||||
|
||||
element = Element(name, xattrs)
|
||||
|
||||
if text:
|
||||
element.text = text
|
||||
|
||||
elif args:
|
||||
for key, value in iteritems(args):
|
||||
key = str(key).replace('_', '-')
|
||||
if isinstance(value, list):
|
||||
for item in value:
|
||||
child = SubElement(element, key)
|
||||
if item is not True:
|
||||
child.text = item
|
||||
else:
|
||||
child = SubElement(element, key)
|
||||
if value is not True:
|
||||
child.text = value
|
||||
|
||||
if fetch_config:
|
||||
reply = get_configuration(module, format=xattrs['format'])
|
||||
else:
|
||||
reply = exec_rpc(module, tostring(element), ignore_warning=False)
|
||||
|
||||
if xattrs['format'] == 'text':
|
||||
if fetch_config:
|
||||
data = reply.find('.//configuration-text')
|
||||
else:
|
||||
data = reply.find('.//output')
|
||||
|
||||
if data is None:
|
||||
module.fail_json(msg=tostring(reply))
|
||||
|
||||
responses.append(data.text.strip())
|
||||
|
||||
elif xattrs['format'] == 'json':
|
||||
responses.append(module.from_json(reply.text.strip()))
|
||||
|
||||
elif xattrs['format'] == 'set':
|
||||
data = reply.find('.//configuration-set')
|
||||
if data is None:
|
||||
module.fail_json(msg="Display format 'set' is not supported by remote device.")
|
||||
responses.append(data.text.strip())
|
||||
|
||||
else:
|
||||
responses.append(tostring(reply))
|
||||
|
||||
return responses
|
||||
|
||||
|
||||
def split(value):
|
||||
lex = shlex.shlex(value)
|
||||
lex.quotes = '"'
|
||||
lex.whitespace_split = True
|
||||
lex.commenters = ''
|
||||
return list(lex)
|
||||
|
||||
|
||||
def parse_rpcs(module):
|
||||
items = list()
|
||||
|
||||
for rpc in (module.params['rpcs'] or list()):
|
||||
parts = shlex.split(rpc)
|
||||
|
||||
name = parts.pop(0)
|
||||
args = dict()
|
||||
|
||||
for item in parts:
|
||||
key, value = item.split('=')
|
||||
if str(value).upper() in ['TRUE', 'FALSE']:
|
||||
args[key] = bool(value)
|
||||
elif re.match(r'^[0-9]+$', value):
|
||||
args[key] = int(value)
|
||||
else:
|
||||
args[key] = str(value)
|
||||
|
||||
display = module.params['display'] or 'xml'
|
||||
|
||||
if display == 'set' and rpc != 'get-configuration':
|
||||
module.fail_json(msg="Invalid display option '%s' given for rpc '%s'" % ('set', name))
|
||||
|
||||
xattrs = {'format': display}
|
||||
items.append({'name': name, 'args': args, 'xattrs': xattrs})
|
||||
|
||||
return items
|
||||
|
||||
|
||||
def parse_commands(module, warnings):
|
||||
items = list()
|
||||
|
||||
for command in (module.params['commands'] or list()):
|
||||
if module.check_mode and not command.startswith('show'):
|
||||
warnings.append(
|
||||
'Only show commands are supported when using check_mode, not '
|
||||
'executing %s' % command
|
||||
)
|
||||
continue
|
||||
|
||||
parts = command.split('|')
|
||||
text = parts[0]
|
||||
|
||||
display = module.params['display'] or 'text'
|
||||
|
||||
if '| display json' in command:
|
||||
display = 'json'
|
||||
|
||||
elif '| display xml' in command:
|
||||
display = 'xml'
|
||||
|
||||
if display == 'set' or '| display set' in command:
|
||||
if command.startswith('show configuration'):
|
||||
display = 'set'
|
||||
else:
|
||||
module.fail_json(msg="Invalid display option '%s' given for command '%s'" % ('set', command))
|
||||
|
||||
xattrs = {'format': display}
|
||||
items.append({'name': 'command', 'xattrs': xattrs, 'text': text})
|
||||
|
||||
return items
|
||||
|
||||
|
||||
def main():
|
||||
"""entry point for module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
commands=dict(type='list'),
|
||||
rpcs=dict(type='list'),
|
||||
|
||||
display=dict(choices=['text', 'json', 'xml', 'set'], aliases=['format', 'output']),
|
||||
|
||||
wait_for=dict(type='list', aliases=['waitfor']),
|
||||
match=dict(default='all', choices=['all', 'any']),
|
||||
|
||||
retries=dict(default=10, type='int'),
|
||||
interval=dict(default=1, type='int')
|
||||
)
|
||||
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
required_one_of = [('commands', 'rpcs')]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
required_one_of=required_one_of,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
conn = get_connection(module)
|
||||
capabilities = get_capabilities(module)
|
||||
|
||||
if capabilities.get('network_api') == 'cliconf':
|
||||
if any((module.params['wait_for'], module.params['match'], module.params['rpcs'])):
|
||||
module.warn('arguments wait_for, match, rpcs are not supported when using transport=cli')
|
||||
commands = module.params['commands']
|
||||
|
||||
output = list()
|
||||
display = module.params['display']
|
||||
for cmd in commands:
|
||||
# if display format is not mentioned in command, add the display format
|
||||
# from the modules params
|
||||
if ('display json' not in cmd) and ('display xml' not in cmd):
|
||||
if display and display != 'text':
|
||||
cmd += ' | display {0}'.format(display)
|
||||
try:
|
||||
output.append(conn.get(command=cmd))
|
||||
except ConnectionError as exc:
|
||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
||||
|
||||
lines = [out.split('\n') for out in output]
|
||||
result = {'changed': False, 'stdout': output, 'stdout_lines': lines}
|
||||
module.exit_json(**result)
|
||||
|
||||
items = list()
|
||||
items.extend(parse_commands(module, warnings))
|
||||
items.extend(parse_rpcs(module))
|
||||
|
||||
wait_for = module.params['wait_for'] or list()
|
||||
conditionals = [Conditional(c) for c in wait_for]
|
||||
|
||||
retries = module.params['retries']
|
||||
interval = module.params['interval']
|
||||
match = module.params['match']
|
||||
|
||||
while retries > 0:
|
||||
responses = rpc(module, items)
|
||||
transformed = list()
|
||||
output = list()
|
||||
for item, resp in zip(items, responses):
|
||||
if item['xattrs']['format'] == 'xml':
|
||||
if not HAS_JXMLEASE:
|
||||
module.fail_json(msg='jxmlease is required but does not appear to be installed. '
|
||||
'It can be installed using `pip install jxmlease`')
|
||||
|
||||
try:
|
||||
json_resp = jxmlease.parse(resp)
|
||||
transformed.append(json_resp)
|
||||
output.append(json_resp)
|
||||
except Exception:
|
||||
raise ValueError(resp)
|
||||
else:
|
||||
transformed.append(resp)
|
||||
|
||||
for item in list(conditionals):
|
||||
try:
|
||||
if item(transformed):
|
||||
if match == 'any':
|
||||
conditionals = list()
|
||||
break
|
||||
conditionals.remove(item)
|
||||
except FailedConditionalError:
|
||||
pass
|
||||
|
||||
if not conditionals:
|
||||
break
|
||||
|
||||
time.sleep(interval)
|
||||
retries -= 1
|
||||
|
||||
if conditionals:
|
||||
failed_conditions = [item.raw for item in conditionals]
|
||||
msg = 'One or more conditional statements have not been satisfied'
|
||||
module.fail_json(msg=msg, failed_conditions=failed_conditions)
|
||||
|
||||
result = {
|
||||
'changed': False,
|
||||
'warnings': warnings,
|
||||
'stdout': responses,
|
||||
'stdout_lines': list(to_lines(responses)),
|
||||
}
|
||||
|
||||
if output:
|
||||
result['output'] = output
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,483 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_config
|
||||
version_added: "2.1"
|
||||
author: "Peter Sprygada (@privateip)"
|
||||
short_description: Manage configuration on devices running Juniper JUNOS
|
||||
description:
|
||||
- This module provides an implementation for working with the active
|
||||
configuration running on Juniper JUNOS devices. It provides a set
|
||||
of arguments for loading configuration, performing rollback operations
|
||||
and zeroing the active configuration on the device.
|
||||
extends_documentation_fragment: junos
|
||||
options:
|
||||
lines:
|
||||
description:
|
||||
- This argument takes a list of C(set) or C(delete) configuration
|
||||
lines to push into the remote device. Each line must start with
|
||||
either C(set) or C(delete). This argument is mutually exclusive
|
||||
with the I(src) argument.
|
||||
src:
|
||||
description:
|
||||
- The I(src) argument provides a path to the configuration file
|
||||
to load into the remote system. The path can either be a full
|
||||
system path to the configuration file if the value starts with /
|
||||
or relative to the root of the implemented role or playbook.
|
||||
This argument is mutually exclusive with the I(lines) argument.
|
||||
version_added: "2.2"
|
||||
src_format:
|
||||
description:
|
||||
- The I(src_format) argument specifies the format of the configuration
|
||||
found int I(src). If the I(src_format) argument is not provided,
|
||||
the module will attempt to determine the format of the configuration
|
||||
file specified in I(src).
|
||||
choices: ['xml', 'set', 'text', 'json']
|
||||
version_added: "2.2"
|
||||
rollback:
|
||||
description:
|
||||
- The C(rollback) argument instructs the module to rollback the
|
||||
current configuration to the identifier specified in the
|
||||
argument. If the specified rollback identifier does not
|
||||
exist on the remote device, the module will fail. To rollback
|
||||
to the most recent commit, set the C(rollback) argument to 0.
|
||||
zeroize:
|
||||
description:
|
||||
- The C(zeroize) argument is used to completely sanitize the
|
||||
remote device configuration back to initial defaults. This
|
||||
argument will effectively remove all current configuration
|
||||
statements on the remote device.
|
||||
type: bool
|
||||
confirm:
|
||||
description:
|
||||
- The C(confirm) argument will configure a time out value in minutes
|
||||
for the commit to be confirmed before it is automatically
|
||||
rolled back. If the C(confirm) argument is set to False, this
|
||||
argument is silently ignored. If the value for this argument
|
||||
is set to 0, the commit is confirmed immediately.
|
||||
default: 0
|
||||
comment:
|
||||
description:
|
||||
- The C(comment) argument specifies a text string to be used
|
||||
when committing the configuration. If the C(confirm) argument
|
||||
is set to False, this argument is silently ignored.
|
||||
default: configured by junos_config
|
||||
replace:
|
||||
description:
|
||||
- The C(replace) argument will instruct the remote device to
|
||||
replace the current configuration hierarchy with the one specified
|
||||
in the corresponding hierarchy of the source configuration loaded
|
||||
from this module.
|
||||
- Note this argument should be considered deprecated. To achieve
|
||||
the equivalent, set the I(update) argument to C(replace). This argument
|
||||
will be removed in a future release. The C(replace) and C(update) argument
|
||||
is mutually exclusive.
|
||||
type: bool
|
||||
default: 'no'
|
||||
backup:
|
||||
description:
|
||||
- This argument will cause the module to create a full backup of
|
||||
the current C(running-config) from the remote device before any
|
||||
changes are made. If the C(backup_options) value is not given,
|
||||
the backup file is written to the C(backup) folder in the playbook
|
||||
root directory or role root directory, if playbook is part of an
|
||||
ansible role. If the directory does not exist, it is created.
|
||||
type: bool
|
||||
default: 'no'
|
||||
version_added: "2.2"
|
||||
update:
|
||||
description:
|
||||
- This argument will decide how to load the configuration
|
||||
data particularly when the candidate configuration and loaded
|
||||
configuration contain conflicting statements. Following are
|
||||
accepted values.
|
||||
C(merge) combines the data in the loaded configuration with the
|
||||
candidate configuration. If statements in the loaded configuration
|
||||
conflict with statements in the candidate configuration, the loaded
|
||||
statements replace the candidate ones.
|
||||
C(override) discards the entire candidate configuration and replaces
|
||||
it with the loaded configuration.
|
||||
C(replace) substitutes each hierarchy level in the loaded configuration
|
||||
for the corresponding level.
|
||||
C(update) is similar to the override option. The new configuration completely
|
||||
replaces the existing configuration. The difference comes when the configuration
|
||||
is later committed. This option performs a 'diff' between the new candidate
|
||||
configuration and the existing committed configuration. It then only notifies
|
||||
system processes responsible for the changed portions of the configuration, and
|
||||
only marks the actual configuration changes as 'changed'.
|
||||
default: merge
|
||||
choices: ['merge', 'override', 'replace', 'update']
|
||||
version_added: "2.3"
|
||||
confirm_commit:
|
||||
description:
|
||||
- This argument will execute commit operation on remote device.
|
||||
It can be used to confirm a previous commit.
|
||||
type: bool
|
||||
default: 'no'
|
||||
version_added: "2.4"
|
||||
check_commit:
|
||||
description:
|
||||
- This argument will check correctness of syntax; do not apply changes.
|
||||
- Note that this argument can be used to confirm verified configuration done via commit confirmed operation
|
||||
type: bool
|
||||
default: 'no'
|
||||
version_added: "2.8"
|
||||
backup_options:
|
||||
description:
|
||||
- This is a dict object containing configurable options related to backup file path.
|
||||
The value of this option is read only when C(backup) is set to I(yes), if C(backup) is set
|
||||
to I(no) this option will be silently ignored.
|
||||
suboptions:
|
||||
filename:
|
||||
description:
|
||||
- The filename to be used to store the backup configuration. If the filename
|
||||
is not given it will be generated based on the hostname, current time and date
|
||||
in format defined by <hostname>_config.<current-date>@<current-time>
|
||||
dir_path:
|
||||
description:
|
||||
- This option provides the path ending with directory name in which the backup
|
||||
configuration file will be stored. If the directory does not exist it will be first
|
||||
created and the filename is either the value of C(filename) or default filename
|
||||
as described in C(filename) options description. If the path value is not given
|
||||
in that case a I(backup) directory will be created in the current working directory
|
||||
and backup configuration will be copied in C(filename) within I(backup) directory.
|
||||
type: path
|
||||
type: dict
|
||||
version_added: "2.8"
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Abbreviated commands are NOT idempotent, see
|
||||
L(Network FAQ,../network/user_guide/faq.html#why-do-the-config-modules-always-return-changed-true-with-abbreviated-commands).
|
||||
- Loading JSON-formatted configuration I(json) is supported
|
||||
starting in Junos OS Release 16.1 onwards.
|
||||
- Update C(override) not currently compatible with C(set) notation.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: load configure file into device
|
||||
junos_config:
|
||||
src: srx.cfg
|
||||
comment: update config
|
||||
|
||||
- name: load configure lines into device
|
||||
junos_config:
|
||||
lines:
|
||||
- set interfaces ge-0/0/1 unit 0 description "Test interface"
|
||||
- set vlans vlan01 description "Test vlan"
|
||||
comment: update config
|
||||
|
||||
- name: Set routed VLAN interface (RVI) IPv4 address
|
||||
junos_config:
|
||||
lines:
|
||||
- set vlans vlan01 vlan-id 1
|
||||
- set interfaces irb unit 10 family inet address 10.0.0.1/24
|
||||
- set vlans vlan01 l3-interface irb.10
|
||||
|
||||
- name: Check correctness of commit configuration
|
||||
junos_config:
|
||||
check_commit: yes
|
||||
|
||||
- name: rollback the configuration to id 10
|
||||
junos_config:
|
||||
rollback: 10
|
||||
|
||||
- name: zero out the current configuration
|
||||
junos_config:
|
||||
zeroize: yes
|
||||
|
||||
- name: Set VLAN access and trunking
|
||||
junos_config:
|
||||
lines:
|
||||
- set vlans vlan02 vlan-id 6
|
||||
- set interfaces ge-0/0/6.0 family ethernet-switching interface-mode access vlan members vlan02
|
||||
- set interfaces ge-0/0/6.0 family ethernet-switching interface-mode trunk vlan members vlan02
|
||||
|
||||
- name: confirm a previous commit
|
||||
junos_config:
|
||||
confirm_commit: yes
|
||||
|
||||
- name: for idempotency, use full-form commands
|
||||
junos_config:
|
||||
lines:
|
||||
# - set int ge-0/0/1 unit 0 desc "Test interface"
|
||||
- set interfaces ge-0/0/1 unit 0 description "Test interface"
|
||||
|
||||
- name: configurable backup path
|
||||
junos_config:
|
||||
src: srx.cfg
|
||||
backup: yes
|
||||
backup_options:
|
||||
filename: backup.cfg
|
||||
dir_path: /home/user
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
backup_path:
|
||||
description: The full path to the backup file
|
||||
returned: when backup is yes
|
||||
type: str
|
||||
sample: /playbooks/ansible/backup/config.2016-07-16@22:28:34
|
||||
filename:
|
||||
description: The name of the backup file
|
||||
returned: when backup is yes and filename is not specified in backup options
|
||||
type: str
|
||||
sample: junos01_config.2016-07-16@22:28:34
|
||||
shortname:
|
||||
description: The full path to the backup file excluding the timestamp
|
||||
returned: when backup is yes and filename is not specified in backup options
|
||||
type: str
|
||||
sample: /playbooks/ansible/backup/junos01_config
|
||||
date:
|
||||
description: The date extracted from the backup file name
|
||||
returned: when backup is yes
|
||||
type: str
|
||||
sample: "2016-07-16"
|
||||
time:
|
||||
description: The time extracted from the backup file name
|
||||
returned: when backup is yes
|
||||
type: str
|
||||
sample: "22:28:34"
|
||||
"""
|
||||
import re
|
||||
import json
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.common.netconf import exec_rpc
|
||||
from ansible.module_utils.network.junos.junos import get_diff, load_config, get_configuration
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, load_configuration, tostring
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.module_utils._text import to_native, to_text
|
||||
|
||||
try:
|
||||
from lxml.etree import Element, fromstring
|
||||
except ImportError:
|
||||
from xml.etree.ElementTree import Element, fromstring
|
||||
|
||||
try:
|
||||
from lxml.etree import ParseError
|
||||
except ImportError:
|
||||
try:
|
||||
from xml.etree.ElementTree import ParseError
|
||||
except ImportError:
|
||||
# for Python < 2.7
|
||||
from xml.parsers.expat import ExpatError
|
||||
ParseError = ExpatError
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
DEFAULT_COMMENT = 'configured by junos_config'
|
||||
|
||||
|
||||
def check_args(module, warnings):
|
||||
if module.params['replace'] is not None:
|
||||
module.fail_json(msg='argument replace is deprecated, use update')
|
||||
|
||||
|
||||
def zeroize(module):
|
||||
return exec_rpc(module, tostring(Element('request-system-zeroize')), ignore_warning=False)
|
||||
|
||||
|
||||
def rollback(ele, id='0'):
|
||||
return get_diff(ele, id)
|
||||
|
||||
|
||||
def guess_format(config):
|
||||
try:
|
||||
json.loads(config)
|
||||
return 'json'
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
fromstring(config)
|
||||
return 'xml'
|
||||
except ParseError:
|
||||
pass
|
||||
|
||||
if config.startswith('set') or config.startswith('delete'):
|
||||
return 'set'
|
||||
|
||||
return 'text'
|
||||
|
||||
|
||||
def filter_delete_statements(module, candidate):
|
||||
reply = get_configuration(module, format='set')
|
||||
match = reply.find('.//configuration-set')
|
||||
if match is None:
|
||||
# Could not find configuration-set in reply, perhaps device does not support it?
|
||||
return candidate
|
||||
config = to_native(match.text, encoding='latin-1')
|
||||
|
||||
modified_candidate = candidate[:]
|
||||
for index, line in reversed(list(enumerate(candidate))):
|
||||
if line.startswith('delete'):
|
||||
newline = re.sub('^delete', 'set', line)
|
||||
if newline not in config:
|
||||
del modified_candidate[index]
|
||||
|
||||
return modified_candidate
|
||||
|
||||
|
||||
def configure_device(module, warnings, candidate):
|
||||
|
||||
kwargs = {}
|
||||
config_format = None
|
||||
|
||||
if module.params['src']:
|
||||
config_format = module.params['src_format'] or guess_format(str(candidate))
|
||||
if config_format == 'set':
|
||||
kwargs.update({'format': 'text', 'action': 'set'})
|
||||
else:
|
||||
kwargs.update({'format': config_format, 'action': module.params['update']})
|
||||
|
||||
if isinstance(candidate, string_types):
|
||||
candidate = candidate.split('\n')
|
||||
|
||||
# this is done to filter out `delete ...` statements which map to
|
||||
# nothing in the config as that will cause an exception to be raised
|
||||
if any((module.params['lines'], config_format == 'set')):
|
||||
candidate = filter_delete_statements(module, candidate)
|
||||
kwargs['format'] = 'text'
|
||||
kwargs['action'] = 'set'
|
||||
|
||||
return load_config(module, candidate, warnings, **kwargs)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
backup_spec = dict(
|
||||
filename=dict(),
|
||||
dir_path=dict(type='path')
|
||||
)
|
||||
argument_spec = dict(
|
||||
lines=dict(aliases=['commands'], type='list'),
|
||||
|
||||
src=dict(type='path'),
|
||||
src_format=dict(choices=['xml', 'text', 'set', 'json']),
|
||||
|
||||
# update operations
|
||||
update=dict(default='merge', choices=['merge', 'override', 'replace', 'update']),
|
||||
|
||||
# deprecated replace in Ansible 2.3
|
||||
replace=dict(type='bool'),
|
||||
|
||||
confirm=dict(default=0, type='int'),
|
||||
comment=dict(default=DEFAULT_COMMENT),
|
||||
confirm_commit=dict(type='bool', default=False),
|
||||
check_commit=dict(type='bool', default=False),
|
||||
|
||||
# config operations
|
||||
backup=dict(type='bool', default=False),
|
||||
backup_options=dict(type='dict', options=backup_spec),
|
||||
rollback=dict(type='int'),
|
||||
|
||||
zeroize=dict(default=False, type='bool'),
|
||||
)
|
||||
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
mutually_exclusive = [('lines', 'src', 'rollback', 'zeroize')]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
check_args(module, warnings)
|
||||
|
||||
candidate = module.params['lines'] or module.params['src']
|
||||
commit = not module.check_mode
|
||||
|
||||
result = {'changed': False, 'warnings': warnings}
|
||||
|
||||
if module.params['backup']:
|
||||
for conf_format in ['set', 'text']:
|
||||
reply = get_configuration(module, format=conf_format)
|
||||
match = reply.find('.//configuration-%s' % conf_format)
|
||||
if match is not None:
|
||||
break
|
||||
else:
|
||||
module.fail_json(msg='unable to retrieve device configuration')
|
||||
|
||||
result['__backup__'] = match.text.strip()
|
||||
|
||||
rollback_id = module.params['rollback']
|
||||
if rollback_id:
|
||||
diff = rollback(module, rollback_id)
|
||||
if commit:
|
||||
kwargs = {
|
||||
'comment': module.params['comment']
|
||||
}
|
||||
with locked_config(module):
|
||||
load_configuration(module, rollback=rollback_id)
|
||||
commit_configuration(module, **kwargs)
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
result['changed'] = True
|
||||
|
||||
elif module.params['zeroize']:
|
||||
if commit:
|
||||
zeroize(module)
|
||||
result['changed'] = True
|
||||
|
||||
else:
|
||||
if candidate:
|
||||
with locked_config(module):
|
||||
diff = configure_device(module, warnings, candidate)
|
||||
if diff:
|
||||
if commit:
|
||||
kwargs = {
|
||||
'comment': module.params['comment'],
|
||||
'check': module.params['check_commit']
|
||||
}
|
||||
|
||||
confirm = module.params['confirm']
|
||||
if confirm > 0:
|
||||
kwargs.update({
|
||||
'confirm': True,
|
||||
'confirm_timeout': to_text(confirm, errors='surrogate_then_replace')
|
||||
})
|
||||
commit_configuration(module, **kwargs)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
elif module.params['check_commit']:
|
||||
commit_configuration(module, check=True)
|
||||
|
||||
elif module.params['confirm_commit']:
|
||||
with locked_config(module):
|
||||
# confirm a previous commit
|
||||
commit_configuration(module)
|
||||
|
||||
result['changed'] = True
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,139 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_facts
|
||||
version_added: "2.1"
|
||||
author: "Nathaniel Case (@Qalthos)"
|
||||
short_description: Collect facts from remote devices running Juniper Junos
|
||||
description:
|
||||
- Collects fact information from a remote device running the Junos
|
||||
operating system. By default, the module will collect basic fact
|
||||
information from the device to be included with the hostvars.
|
||||
Additional fact information can be collected based on the
|
||||
configured set of arguments.
|
||||
extends_documentation_fragment: junos
|
||||
options:
|
||||
gather_subset:
|
||||
description:
|
||||
- When supplied, this argument will restrict the facts collected
|
||||
to a given subset. Possible values for this argument include
|
||||
all, hardware, config, and interfaces. Can specify a list of
|
||||
values to include a larger subset. Values can also be used
|
||||
with an initial C(M(!)) to specify that a specific subset should
|
||||
not be collected. To maintain backward compatibility old style facts
|
||||
can be retrieved by explicitly adding C(ofacts) to value, this requires
|
||||
junos-eznc to be installed as a prerequisite. Valid value of gather_subset
|
||||
are default, hardware, config, interfaces, ofacts. If C(ofacts) is present in the
|
||||
list it fetches the old style facts (fact keys without 'ansible_' prefix) and it requires
|
||||
junos-eznc library to be installed on control node and the device login credentials
|
||||
must be given in C(provider) option.
|
||||
required: false
|
||||
default: ['!config']
|
||||
version_added: "2.3"
|
||||
config_format:
|
||||
description:
|
||||
- The I(config_format) argument specifies the format of the configuration
|
||||
when serializing output from the device. This argument is applicable
|
||||
only when C(config) value is present in I(gather_subset).
|
||||
The I(config_format) should be supported by the junos version running on
|
||||
device. This value is not applicable while fetching old style facts that is
|
||||
when C(ofacts) value is present in value if I(gather_subset) value. This option
|
||||
is valid only for C(gather_subset) values.
|
||||
required: false
|
||||
default: 'text'
|
||||
choices: ['xml', 'text', 'set', 'json']
|
||||
version_added: "2.3"
|
||||
gather_network_resources:
|
||||
description:
|
||||
- When supplied, this argument will restrict the facts collected
|
||||
to a given subset. Possible values for this argument include
|
||||
all and the resources like interfaces, vlans etc.
|
||||
Can specify a list of values to include a larger subset.
|
||||
Values can also be used with an initial C(M(!)) to specify that
|
||||
a specific subset should not be collected.
|
||||
Valid subsets are 'all', 'interfaces', 'lacp', 'lacp_interfaces',
|
||||
'lag_interfaces', 'l2_interfaces', 'l3_interfaces', 'lldp_global',
|
||||
'lldp_interfaces', 'vlans'.
|
||||
required: false
|
||||
version_added: "2.9"
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- Ensure I(config_format) used to retrieve configuration from device
|
||||
is supported by junos version running on device.
|
||||
- With I(config_format = json), configuration in the results will be a dictionary(and not a JSON string)
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
- Fetching old style facts requires junos-eznc library to be installed on control node and the device login credentials
|
||||
must be given in provider option.
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: collect default set of facts
|
||||
junos_facts:
|
||||
|
||||
- name: collect default set of facts and configuration
|
||||
junos_facts:
|
||||
gather_subset: config
|
||||
|
||||
- name: Gather legacy and resource facts
|
||||
junos_facts:
|
||||
gather_subset: all
|
||||
gather_network_resources: all
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
ansible_facts:
|
||||
description: Returns the facts collect from the device
|
||||
returned: always
|
||||
type: dict
|
||||
"""
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.argspec.facts.facts import FactsArgs
|
||||
from ansible.module_utils.network.junos.facts.facts import Facts
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for module execution
|
||||
|
||||
:returns: ansible_facts
|
||||
"""
|
||||
argument_spec = FactsArgs.argument_spec
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = []
|
||||
if module.params["gather_subset"] == "!config":
|
||||
warnings.append('default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards')
|
||||
|
||||
result = Facts(module).get_facts()
|
||||
|
||||
ansible_facts, additional_warnings = result
|
||||
warnings.extend(additional_warnings)
|
||||
|
||||
module.exit_json(ansible_facts=ansible_facts, warnings=warnings)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,332 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The module file for junos_interfaces
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_interfaces
|
||||
version_added: 2.9
|
||||
short_description: Manages interface attributes of Juniper Junos OS network devices.
|
||||
description: This module manages the interfaces on Juniper Junos OS network devices.
|
||||
author: Ganesh Nalawade (@ganeshrn)
|
||||
options:
|
||||
config:
|
||||
description: The provided configuration
|
||||
type: list
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Full name of interface, e.g. ge-0/0/0.
|
||||
type: str
|
||||
required: True
|
||||
description:
|
||||
description:
|
||||
- Interface description.
|
||||
type: str
|
||||
duplex:
|
||||
description:
|
||||
- Interface link status. Applicable for Ethernet interfaces only, either in half duplex,
|
||||
full duplex or in automatic state which negotiates the duplex automatically.
|
||||
type: str
|
||||
choices: ['automatic', 'full-duplex', 'half-duplex']
|
||||
enabled:
|
||||
default: True
|
||||
description:
|
||||
- Administrative state of the interface.
|
||||
- Set the value to C(true) to administratively enabled the interface or C(false) to disable it.
|
||||
type: bool
|
||||
hold_time:
|
||||
description:
|
||||
- The hold time for given interface name.
|
||||
type: dict
|
||||
suboptions:
|
||||
down:
|
||||
description:
|
||||
- The link down hold time in milliseconds.
|
||||
type: int
|
||||
up:
|
||||
description:
|
||||
- The link up hold time in milliseconds.
|
||||
type: int
|
||||
mtu:
|
||||
description:
|
||||
- MTU for a specific interface.
|
||||
- Applicable for Ethernet interfaces only.
|
||||
type: int
|
||||
speed:
|
||||
description:
|
||||
- Interface link speed. Applicable for Ethernet interfaces only.
|
||||
type: int
|
||||
state:
|
||||
choices:
|
||||
- merged
|
||||
- replaced
|
||||
- overridden
|
||||
- deleted
|
||||
default: merged
|
||||
description:
|
||||
- The state of the configuration after module completion
|
||||
type: str
|
||||
requirements:
|
||||
- ncclient (>=v0.6.4)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 18.4R1.
|
||||
- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
|
||||
"""
|
||||
EXAMPLES = """
|
||||
# Using deleted
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Configured by Ansible-1";
|
||||
# speed 1g;
|
||||
# mtu 1800
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "Configured by Ansible-2";
|
||||
# ether-options {
|
||||
# auto-negotiation;
|
||||
# }
|
||||
# }
|
||||
|
||||
- name: "Delete given options for the interface (Note: This won't delete the interface itself if any other values are configured for interface)"
|
||||
junos_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/1
|
||||
description: 'Configured by Ansible-1'
|
||||
speed: 1g
|
||||
mtu: 1800
|
||||
- name: ge-0/0/2
|
||||
description: 'Configured by Ansible -2'
|
||||
state: deleted
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/2 {
|
||||
# ether-options {
|
||||
# auto-negotiation;
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
# Using merged
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "test interface";
|
||||
# speed 1g;
|
||||
# }
|
||||
|
||||
- name: "Merge provided configuration with device configuration (default operation is merge)"
|
||||
junos_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/1
|
||||
description: 'Configured by Ansible-1'
|
||||
enabled: True
|
||||
mtu: 1800
|
||||
- name: ge-0/0/2
|
||||
description: 'Configured by Ansible-2'
|
||||
enabled: False
|
||||
state: merged
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Configured by Ansible-1";
|
||||
# speed 1g;
|
||||
# mtu 1800
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# disable;
|
||||
# description "Configured by Ansible-2";
|
||||
# }
|
||||
|
||||
|
||||
# Using overridden
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Configured by Ansible-1";
|
||||
# speed 1g;
|
||||
# mtu 1800
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# disable;
|
||||
# description "Configured by Ansible-2";
|
||||
# ether-options {
|
||||
# auto-negotiation;
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/11 {
|
||||
# description "Configured by Ansible-11";
|
||||
# }
|
||||
|
||||
- name: "Override device configuration of all interfaces with provided configuration"
|
||||
junos_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/2
|
||||
description: 'Configured by Ansible-2'
|
||||
enabled: False
|
||||
mtu: 2800
|
||||
- name: ge-0/0/3
|
||||
description: 'Configured by Ansible-3'
|
||||
state: overridden
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/2 {
|
||||
# disable;
|
||||
# description "Configured by Ansible-2";
|
||||
# mtu 2800
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# description "Configured by Ansible-3";
|
||||
# }
|
||||
|
||||
|
||||
# Using replaced
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Configured by Ansible-1";
|
||||
# speed 1g;
|
||||
# mtu 1800
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# disable;
|
||||
# mtu 1800;
|
||||
# speed 1g;
|
||||
# description "Configured by Ansible-2";
|
||||
# ether-options {
|
||||
# auto-negotiation;
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/11 {
|
||||
# description "Configured by Ansible-11";
|
||||
# }
|
||||
|
||||
- name: "Replaces device configuration of listed interfaces with provided configuration"
|
||||
junos_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/2
|
||||
description: 'Configured by Ansible-2'
|
||||
enabled: False
|
||||
mtu: 2800
|
||||
- name: ge-0/0/3
|
||||
description: 'Configured by Ansible-3'
|
||||
state: replaced
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Configured by Ansible-1";
|
||||
# speed 1g;
|
||||
# mtu 1800
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# disable;
|
||||
# description "Configured by Ansible-2";
|
||||
# mtu 2800
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# description "Configured by Ansible-3";
|
||||
# }
|
||||
# ge-0/0/11 {
|
||||
# description "Configured by Ansible-11";
|
||||
# }
|
||||
|
||||
|
||||
"""
|
||||
RETURN = """
|
||||
before:
|
||||
description: The configuration as structured data prior to module invocation.
|
||||
returned: always
|
||||
type: list
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
after:
|
||||
description: The configuration as structured data after module completion.
|
||||
returned: when changed
|
||||
type: list
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
xml:
|
||||
description: The set of xml rpc payload pushed to the remote device.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['xml 1', 'xml 2', 'xml 3']
|
||||
"""
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.argspec.interfaces.interfaces import InterfacesArgs
|
||||
from ansible.module_utils.network.junos.config.interfaces.interfaces import Interfaces
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for module execution
|
||||
|
||||
:returns: the result form module invocation
|
||||
"""
|
||||
required_if = [('state', 'merged', ('config',)),
|
||||
('state', 'replaced', ('config',)),
|
||||
('state', 'overridden', ('config',))]
|
||||
|
||||
module = AnsibleModule(argument_spec=InterfacesArgs.argument_spec,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
result = Interfaces(module).execute_module()
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,407 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The module file for junos_l2_interfaces
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_l2_interfaces
|
||||
version_added: 2.9
|
||||
short_description: Manage Layer-2 interface on Juniper JUNOS devices
|
||||
description: This module provides declarative management of a Layer-2 interface on Juniper JUNOS devices.
|
||||
author: Ganesh Nalawade (@ganeshrn)
|
||||
options:
|
||||
config:
|
||||
description: A dictionary of Layer-2 interface options
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Full name of interface, e.g. ge-0/0/1.
|
||||
type: str
|
||||
required: True
|
||||
unit:
|
||||
description:
|
||||
- Logical interface number. Value of C(unit) should be of type
|
||||
integer.
|
||||
type: int
|
||||
access:
|
||||
description:
|
||||
- Configure the interface as a Layer 2 access mode.
|
||||
type: dict
|
||||
suboptions:
|
||||
vlan:
|
||||
description:
|
||||
- Configure the access VLAN ID.
|
||||
type: str
|
||||
trunk:
|
||||
description:
|
||||
- Configure the interface as a Layer 2 trunk mode.
|
||||
type: dict
|
||||
suboptions:
|
||||
allowed_vlans:
|
||||
description:
|
||||
- List of VLANs to be configured in trunk port. It's used as the VLAN range to ADD or
|
||||
REMOVE from the trunk.
|
||||
type: list
|
||||
native_vlan:
|
||||
description:
|
||||
- Native VLAN to be configured in trunk port. It is used as the trunk native VLAN ID.
|
||||
type: str
|
||||
enhanced_layer:
|
||||
description:
|
||||
- True if your device has Enhanced Layer 2 Software (ELS). If the l2 configuration is under
|
||||
C(interface-mode) the value is True else if the l2 configuration is under C(port-mode) value
|
||||
is False
|
||||
type: bool
|
||||
state:
|
||||
choices:
|
||||
- merged
|
||||
- replaced
|
||||
- overridden
|
||||
- deleted
|
||||
default: merged
|
||||
description:
|
||||
- The state of the configuration after module completion
|
||||
type: str
|
||||
requirements:
|
||||
- ncclient (>=v0.6.4)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 18.4R1.
|
||||
- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
|
||||
"""
|
||||
EXAMPLES = """
|
||||
# Using deleted
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
#
|
||||
# ansible@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "L2 interface";
|
||||
# speed 1g;
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode access;
|
||||
# vlan {
|
||||
# members vlan30;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
#}
|
||||
#ge-0/0/2 {
|
||||
# description "non L2 interface";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# address 192.168.56.14/24;
|
||||
# }
|
||||
# }
|
||||
|
||||
- name: "Delete L2 attributes of given interfaces (Note: This won't delete the interface itself)."
|
||||
junos_l2_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/1
|
||||
- name: ge-0/0/2
|
||||
state: deleted
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
#
|
||||
# ansible@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "L2 interface";
|
||||
# speed 1g;
|
||||
# }
|
||||
#ge-0/0/2 {
|
||||
# description "non L2 interface";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# address 192.168.56.14/24;
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
# Using merged
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# ansible@junos01# show interfaces
|
||||
# ge-0/0/3 {
|
||||
# description "test interface";
|
||||
# speed 1g;
|
||||
#}
|
||||
# ge-0/0/4 {
|
||||
# description interface-trunk;
|
||||
# native-vlan-id 100;
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode trunk;
|
||||
# vlan {
|
||||
# members [ vlan40 ];
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
- name: "Merge provided configuration with device configuration (default operation is merge)"
|
||||
junos_l2_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/3
|
||||
access:
|
||||
vlan: v101
|
||||
- name: ge-0/0/4
|
||||
trunk:
|
||||
allowed_vlans:
|
||||
- vlan30
|
||||
native_vlan: 50
|
||||
state: merged
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/3 {
|
||||
# description "test interface";
|
||||
# speed 1g;
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode access;
|
||||
# vlan {
|
||||
# members v101;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/4 {
|
||||
# description interface-trunk;
|
||||
# native-vlan-id 50;
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode trunk;
|
||||
# vlan {
|
||||
# members [ vlan40 vlan30 ];
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
# Using overridden
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# ansible@junos01# show interfaces
|
||||
# ge-0/0/3 {
|
||||
# description "test interface";
|
||||
# speed 1g;
|
||||
#}
|
||||
# ge-0/0/4 {
|
||||
# description interface-trunk;
|
||||
# native-vlan-id 100;
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode trunk;
|
||||
# vlan {
|
||||
# members [ vlan40 ];
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/5 {
|
||||
# description "Configured by Ansible-11";
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode access;
|
||||
# vlan {
|
||||
# members v101;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
- name: "Override provided configuration with device configuration"
|
||||
junos_l2_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/3
|
||||
access:
|
||||
vlan: v101
|
||||
- name: ge-0/0/4
|
||||
trunk:
|
||||
allowed_vlans:
|
||||
- vlan30
|
||||
native_vlan: 50
|
||||
state: overridden
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/3 {
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode access;
|
||||
# vlan {
|
||||
# members v101;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/4 {
|
||||
# description interface-trunk;
|
||||
# native-vlan-id 50;
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode trunk;
|
||||
# vlan {
|
||||
# members [ vlan30 ];
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
# Using replaced
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# ansible@junos01# show interfaces
|
||||
# ge-0/0/3 {
|
||||
# description "test interface";
|
||||
# speed 1g;
|
||||
#}
|
||||
# ge-0/0/4 {
|
||||
# description interface-trunk;
|
||||
# native-vlan-id 100;
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode trunk;
|
||||
# vlan {
|
||||
# members [ vlan40 ];
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
- name: "Replace provided configuration with device configuration"
|
||||
junos_l2_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/3
|
||||
access:
|
||||
vlan: v101
|
||||
- name: ge-0/0/4
|
||||
trunk:
|
||||
allowed_vlans:
|
||||
- vlan30
|
||||
native_vlan: 50
|
||||
state: replaced
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/3 {
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode access;
|
||||
# vlan {
|
||||
# members v101;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/4 {
|
||||
# description interface-trunk;
|
||||
# native-vlan-id 50;
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode trunk;
|
||||
# vlan {
|
||||
# members [ vlan30 ];
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
"""
|
||||
RETURN = """
|
||||
before:
|
||||
description: The configuration as structured data prior to module invocation.
|
||||
returned: always
|
||||
type: list
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
after:
|
||||
description: The configuration as structured data after module completion.
|
||||
returned: when changed
|
||||
type: list
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
commands:
|
||||
description: The set of commands pushed to the remote device.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['command 1', 'command 2', 'command 3']
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.argspec.l2_interfaces.l2_interfaces import L2_interfacesArgs
|
||||
from ansible.module_utils.network.junos.config.l2_interfaces.l2_interfaces import L2_interfaces
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for module execution
|
||||
|
||||
:returns: the result form module invocation
|
||||
"""
|
||||
required_if = [('state', 'merged', ('config',)),
|
||||
('state', 'replaced', ('config',)),
|
||||
('state', 'overridden', ('config',))]
|
||||
|
||||
module = AnsibleModule(argument_spec=L2_interfacesArgs.argument_spec,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
result = L2_interfaces(module).execute_module()
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,408 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The module file for junos_l3_interfaces
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_l3_interfaces
|
||||
version_added: 2.9
|
||||
short_description: Manage Layer 3 interface on Juniper JUNOS devices
|
||||
description: This module provides declarative management of a Layer 3 interface on Juniper JUNOS devices
|
||||
author: Daniel Mellado (@dmellado)
|
||||
requirements:
|
||||
- ncclient (>=v0.6.4)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on the device being managed.
|
||||
- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- Tested against JunOS v18.4R1
|
||||
options:
|
||||
config:
|
||||
description: A dictionary of Layer 3 interface options
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Full name of interface, e.g. ge-0/0/1
|
||||
type: str
|
||||
required: True
|
||||
unit:
|
||||
description:
|
||||
- Logical interface number. Value of C(unit) should be of type integer
|
||||
default: 0
|
||||
type: int
|
||||
ipv4:
|
||||
description:
|
||||
- IPv4 addresses to be set for the Layer 3 logical interface mentioned in I(name) option.
|
||||
The address format is <ipv4 address>/<mask>. The mask is number in range 0-32
|
||||
for example, 192.0.2.1/24, or C(dhcp) to query DHCP for an IP address
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- IPv4 address to be set for the specific interface
|
||||
type: str
|
||||
ipv6:
|
||||
description:
|
||||
- IPv6 addresses to be set for the Layer 3 logical interface mentioned in I(name) option.
|
||||
The address format is <ipv6 address>/<mask>, the mask is number in range 0-128
|
||||
for example, 2001:db8:2201:1::1/64 or C(auto-config) to use SLAAC
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- IPv6 address to be set for the specific interface
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- The state of the configuration after module completion
|
||||
type: str
|
||||
choices:
|
||||
- merged
|
||||
- replaced
|
||||
- overridden
|
||||
- deleted
|
||||
default: merged
|
||||
"""
|
||||
EXAMPLES = """
|
||||
# Using deleted
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
#
|
||||
# admin# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "L3 interface";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# address 10.200.16.10/24;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "non L3 interface";
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode access;
|
||||
# vlan {
|
||||
# members 2;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
- name: Delete JUNOS L3 logical interface
|
||||
junos_l3_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/1
|
||||
- name: ge-0/0/2
|
||||
state: deleted
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
#
|
||||
# admin# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "deleted L3 interface";
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "non L3 interface";
|
||||
# unit 0 {
|
||||
# family ethernet-switching {
|
||||
# interface-mode access;
|
||||
# vlan {
|
||||
# members 2;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
# Using merged
|
||||
|
||||
# Before state
|
||||
# ------------
|
||||
#
|
||||
# admin# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "L3 interface";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# address 10.200.16.10/24;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "non configured interface";
|
||||
# unit 0;
|
||||
# }
|
||||
|
||||
- name: Merge provided configuration with device configuration (default operation is merge)
|
||||
junos_l3_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/1
|
||||
ipv4:
|
||||
- address: 192.168.1.10/24
|
||||
ipv6:
|
||||
- address: 8d8d:8d01::1/64
|
||||
- name: ge-0/0/2
|
||||
ipv4:
|
||||
- address: dhcp
|
||||
state: merged
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
#
|
||||
# admin# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "L3 interface";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# address 10.200.16.10/24;
|
||||
# address 192.168.1.10/24;
|
||||
# }
|
||||
# family inet6 {
|
||||
# address 8d8d:8d01::1/64;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "L3 interface with dhcp";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# dhcp;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
# Using overridden
|
||||
|
||||
# Before state
|
||||
# ------------
|
||||
#
|
||||
# admin# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "L3 interface";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# address 10.200.16.10/24;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "L3 interface with dhcp";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# dhcp;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# description "another L3 interface";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# address 192.168.1.10/24;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
- name: Override provided configuration with device configuration
|
||||
junos_l3_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/1
|
||||
ipv4:
|
||||
- address: 192.168.1.10/24
|
||||
ipv6:
|
||||
- address: 8d8d:8d01::1/64
|
||||
- name: ge-0/0/2
|
||||
ipv6:
|
||||
- address: 2001:db8:3000::/64
|
||||
state: overridden
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
#
|
||||
# admin# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "L3 interface";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# address 192.168.1.10/24;
|
||||
# }
|
||||
# family inet6 {
|
||||
# address 8d8d:8d01::1/64;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "L3 interface with ipv6";
|
||||
# unit 0 {
|
||||
# family inet6 {
|
||||
# address 2001:db8:3000::/64;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# description "overridden L3 interface";
|
||||
# unit 0;
|
||||
# }
|
||||
|
||||
|
||||
# Using replaced
|
||||
|
||||
# Before state
|
||||
# ------------
|
||||
#
|
||||
# admin# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "L3 interface";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# address 10.200.16.10/24;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "non configured interface";
|
||||
# unit 0;
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# description "another L3 interface";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# address 192.168.1.10/24;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
- name: Replace provided configuration with device configuration
|
||||
junos_l3_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/1
|
||||
ipv4:
|
||||
- address: 192.168.1.10/24
|
||||
ipv6:
|
||||
- address: 8d8d:8d01::1/64
|
||||
- name: ge-0/0/2
|
||||
ipv4:
|
||||
- address: dhcp
|
||||
state: replaced
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
#
|
||||
# admin# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "L3 interface";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# address 192.168.1.10/24;
|
||||
# }
|
||||
# family inet6 {
|
||||
# address 8d8d:8d01::1/64;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "L3 interface with dhcp";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# dhcp;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# description "another L3 interface";
|
||||
# unit 0 {
|
||||
# family inet {
|
||||
# address 192.168.1.10/24;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
"""
|
||||
RETURN = """
|
||||
before:
|
||||
description: The configuration as structured data prior to module invocation.
|
||||
returned: always
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
type: list
|
||||
after:
|
||||
description: The configuration as structured data after module completion.
|
||||
returned: when changed
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
type: list
|
||||
commands:
|
||||
description: The set of commands pushed to the remote device.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['command 1', 'command 2', 'command 3']
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.argspec.l3_interfaces.l3_interfaces import L3_interfacesArgs
|
||||
from ansible.module_utils.network.junos.config.l3_interfaces.l3_interfaces import L3_interfaces
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for module execution
|
||||
|
||||
:returns: the result form module invocation
|
||||
"""
|
||||
required_if = [('state', 'merged', ('config',)),
|
||||
('state', 'replaced', ('config',)),
|
||||
('state', 'overridden', ('config',))]
|
||||
|
||||
module = AnsibleModule(argument_spec=L3_interfacesArgs.argument_spec,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
result = L3_interfaces(module).execute_module()
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,193 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The module file for junos_lacp
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_lacp
|
||||
version_added: 2.9
|
||||
short_description: Manage Global Link Aggregation Control Protocol (LACP) on Juniper Junos devices
|
||||
description: This module provides declarative management of global LACP on Juniper Junos network devices.
|
||||
author: Ganesh Nalawade (@ganeshrn)
|
||||
options:
|
||||
config:
|
||||
description: A dictionary of LACP global options
|
||||
type: dict
|
||||
suboptions:
|
||||
system_priority:
|
||||
description:
|
||||
- LACP priority for the system.
|
||||
type: int
|
||||
link_protection:
|
||||
description:
|
||||
- Enable LACP link-protection for the system. If the value is set to C(non-revertive)
|
||||
it will not revert links when a better priority link comes up. By default the link will
|
||||
be reverted.
|
||||
type: str
|
||||
choices: ['revertive', 'non-revertive']
|
||||
state:
|
||||
description:
|
||||
- The state of the configuration after module completion
|
||||
type: str
|
||||
choices:
|
||||
- merged
|
||||
- replaced
|
||||
- deleted
|
||||
default: merged
|
||||
requirements:
|
||||
- ncclient (>=v0.6.4)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 18.1R1.
|
||||
- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
"""
|
||||
EXAMPLES = """
|
||||
# Using deleted
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show chassis aggregated-devices ethernet lacp
|
||||
# system-priority 63;
|
||||
# link-protection {
|
||||
# non-revertive;
|
||||
# }
|
||||
|
||||
- name: Delete global LACP attributes
|
||||
junos_lacp:
|
||||
state: deleted
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
# user@junos01# show chassis aggregated-devices ethernet lacp
|
||||
#
|
||||
|
||||
|
||||
# Using merged
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show chassis aggregated-devices ethernet lacp
|
||||
#
|
||||
|
||||
- name: Merge global LACP attributes
|
||||
junos_lacp:
|
||||
config:
|
||||
system_priority: 63
|
||||
link_protection: revertive
|
||||
state: merged
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
# user@junos01# show chassis aggregated-devices ethernet lacp
|
||||
# system-priority 63;
|
||||
# link-protection {
|
||||
# non-revertive;
|
||||
# }
|
||||
|
||||
|
||||
# Using replaced
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show chassis aggregated-devices ethernet lacp
|
||||
# system-priority 63;
|
||||
# link-protection {
|
||||
# non-revertive;
|
||||
# }
|
||||
|
||||
- name: Replace global LACP attributes
|
||||
junos_lacp:
|
||||
config:
|
||||
system_priority: 30
|
||||
link_protection: non-revertive
|
||||
state: replaced
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
# user@junos01# show chassis aggregated-devices ethernet lacp
|
||||
# system-priority 30;
|
||||
# link-protection;
|
||||
|
||||
|
||||
"""
|
||||
RETURN = """
|
||||
before:
|
||||
description: The configuration as structured data prior to module invocation.
|
||||
returned: always
|
||||
type: dict
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
after:
|
||||
description: The configuration as structured data after module completion.
|
||||
returned: when changed
|
||||
type: dict
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
xml:
|
||||
description: The set of xml rpc payload pushed to the remote device.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['xml 1', 'xml 2', 'xml 3']
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.argspec.lacp.lacp import LacpArgs
|
||||
from ansible.module_utils.network.junos.config.lacp.lacp import Lacp
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for module execution
|
||||
|
||||
:returns: the result form module invocation
|
||||
"""
|
||||
required_if = [('state', 'merged', ('config',)),
|
||||
('state', 'replaced', ('config',))]
|
||||
|
||||
module = AnsibleModule(argument_spec=LacpArgs.argument_spec,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
result = Lacp(module).execute_module()
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,520 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The module file for junos_lacp_interfaces
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_lacp_interfaces
|
||||
version_added: 2.9
|
||||
short_description: Manage Link Aggregation Control Protocol (LACP) attributes of interfaces on Juniper JUNOS devices.
|
||||
description:
|
||||
- This module manages Link Aggregation Control Protocol (LACP) attributes of interfaces on Juniper JUNOS devices.
|
||||
author: Ganesh Nalawade (@ganeshrn)
|
||||
options:
|
||||
config:
|
||||
description: The list of dictionaries of LACP interfaces options.
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Name Identifier of the interface or link aggregation group.
|
||||
type: str
|
||||
period:
|
||||
description:
|
||||
- Timer interval for periodic transmission of LACP packets. If the value is
|
||||
set to C(fast) the packets are received every second and if the value is
|
||||
C(slow) the packets are received every 30 seconds. This value is applicable
|
||||
for aggregate interface only.
|
||||
type: str
|
||||
choices: ['fast', 'slow']
|
||||
sync_reset:
|
||||
description:
|
||||
- The argument notifies minimum-link failure out of sync to peer. If the value
|
||||
is C(disable) it disables minimum-link failure handling at LACP level and if
|
||||
value is C(enable) it enables minimum-link failure handling at LACP level.
|
||||
This value is applicable for aggregate interface only.
|
||||
type: str
|
||||
choices: ['disable', 'enable']
|
||||
force_up:
|
||||
description:
|
||||
- This is a boolean argument to control if the port should be up in absence
|
||||
of received link Aggregation Control Protocol Data Unit (LACPDUS).
|
||||
This value is applicable for member interfaces only.
|
||||
type: bool
|
||||
port_priority:
|
||||
description:
|
||||
- Priority of the member port. This value is applicable for member interfaces only.
|
||||
- Refer to vendor documentation for valid values.
|
||||
type: int
|
||||
system:
|
||||
description:
|
||||
- This dict object contains configurable options related to LACP
|
||||
system parameters for the link aggregation group.
|
||||
This value is applicable for aggregate interface only.
|
||||
type: dict
|
||||
suboptions:
|
||||
priority:
|
||||
description:
|
||||
- Specifies the system priority to use in LACP negotiations for
|
||||
the bundle.
|
||||
- Refer to vendor documentation for valid values.
|
||||
type: int
|
||||
mac:
|
||||
description:
|
||||
- Specifies the system ID to use in LACP negotiations for
|
||||
the bundle, encoded as a MAC address.
|
||||
type: dict
|
||||
suboptions:
|
||||
address:
|
||||
description:
|
||||
- The system ID to use in LACP negotiations.
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- The state of the configuration after module completion.
|
||||
type: str
|
||||
choices:
|
||||
- merged
|
||||
- replaced
|
||||
- overridden
|
||||
- deleted
|
||||
default: merged
|
||||
"""
|
||||
EXAMPLES = """
|
||||
# Using merged
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/2 {
|
||||
# ether-options {
|
||||
# 802.3ad ae4;
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# ether-options {
|
||||
# 802.3ad ae0;
|
||||
# }
|
||||
# }
|
||||
# ae0 {
|
||||
# description "lag interface merged";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae4 {
|
||||
# description "test aggregate interface";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# link-protection;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
- name: Merge provided configuration with device configuration
|
||||
junos_lacp_interfaces:
|
||||
config:
|
||||
- name: ae0
|
||||
period: fast
|
||||
sync_reset: enable
|
||||
system:
|
||||
priority: 100
|
||||
mac:
|
||||
address: 00:00:00:00:00:02
|
||||
- name: ge-0/0/3
|
||||
port_priority: 100
|
||||
force_up: True
|
||||
state: merged
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/2 {
|
||||
# ether-options {
|
||||
# 802.3ad ae4;
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# ether-options {
|
||||
# 802.3ad {
|
||||
# lacp {
|
||||
# force-up;
|
||||
# port-priority 100;
|
||||
# }
|
||||
# ae0;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae0 {
|
||||
# description "lag interface merged";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# periodic fast;
|
||||
# sync-reset enable;
|
||||
# system-priority 100;
|
||||
# system-id 00:00:00:00:00:02;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae4 {
|
||||
# description "test aggregate interface";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# link-protection;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
# Using replaced
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/2 {
|
||||
# ether-options {
|
||||
# 802.3ad ae4;
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# ether-options {
|
||||
# 802.3ad {
|
||||
# lacp {
|
||||
# force-up;
|
||||
# port-priority 100;
|
||||
# }
|
||||
# ae0;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae0 {
|
||||
# description "lag interface merged";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# periodic fast;
|
||||
# sync-reset enable;
|
||||
# system-priority 100;
|
||||
# system-id 00:00:00:00:00:02;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae4 {
|
||||
# description "test aggregate interface";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# link-protection;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
- name: Replace device LACP interfaces configuration with provided configuration
|
||||
junos_lacp_interfaces:
|
||||
config:
|
||||
- name: ae0
|
||||
period: slow
|
||||
state: replaced
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/2 {
|
||||
# ether-options {
|
||||
# 802.3ad ae4;
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# ether-options {
|
||||
# 802.3ad {
|
||||
# lacp {
|
||||
# force-up;
|
||||
# port-priority 100;
|
||||
# }
|
||||
# ae0;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae0 {
|
||||
# description "lag interface merged";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# periodic slow;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae4 {
|
||||
# description "test aggregate interface";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# link-protection;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
# Using overridden
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/2 {
|
||||
# ether-options {
|
||||
# 802.3ad ae4;
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# ether-options {
|
||||
# 802.3ad {
|
||||
# lacp {
|
||||
# force-up;
|
||||
# port-priority 100;
|
||||
# }
|
||||
# ae0;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae0 {
|
||||
# description "lag interface merged";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# periodic slow;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae4 {
|
||||
# description "test aggregate interface";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# link-protection;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
- name: Overrides all device LACP interfaces configuration with provided configuration
|
||||
junos_lacp_interfaces:
|
||||
config:
|
||||
- name: ae0
|
||||
system:
|
||||
priority: 300
|
||||
mac:
|
||||
address: 00:00:00:00:00:03
|
||||
- name: ge-0/0/2
|
||||
port_priority: 200
|
||||
force_up: False
|
||||
state: overridden
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/2 {
|
||||
# ether-options {
|
||||
# 802.3ad {
|
||||
# lacp {
|
||||
# port-priority 200;
|
||||
# }
|
||||
# ae4;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# ether-options {
|
||||
# 802.3ad {
|
||||
# lacp {
|
||||
# force-up;
|
||||
# port-priority 100;
|
||||
# }
|
||||
# ae0;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae0 {
|
||||
# description "lag interface merged";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# system-priority 300;
|
||||
# system-id 00:00:00:00:00:03;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae4 {
|
||||
# description "test aggregate interface";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# link-protection;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
# Using deleted
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/2 {
|
||||
# ether-options {
|
||||
# 802.3ad {
|
||||
# lacp {
|
||||
# port-priority 200;
|
||||
# }
|
||||
# ae4;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# ether-options {
|
||||
# 802.3ad {
|
||||
# lacp {
|
||||
# force-up;
|
||||
# port-priority 100;
|
||||
# }
|
||||
# ae0;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae0 {
|
||||
# description "lag interface merged";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# system-priority 300;
|
||||
# system-id 00:00:00:00:00:03;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae4 {
|
||||
# description "test aggregate interface";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# link-protection;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
- name: "Delete LACP interfaces attributes of given interfaces (Note: This won't delete the interface itself)"
|
||||
junos_lacp_interfaces:
|
||||
config:
|
||||
- name: ae0
|
||||
- name: ge-0/0/3
|
||||
- name: ge-0/0/2
|
||||
state: deleted
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/2 {
|
||||
# ether-options {
|
||||
# 802.3ad ae4;
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# ether-options {
|
||||
# 802.3ad ae0;
|
||||
# }
|
||||
# }
|
||||
# ae0 {
|
||||
# description "lag interface merged";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ae4 {
|
||||
# description "test aggregate interface";
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# passive;
|
||||
# link-protection;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
before:
|
||||
description: The configuration as structured data prior to module invocation.
|
||||
returned: always
|
||||
type: list
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
after:
|
||||
description: The configuration as structured data after module completion.
|
||||
returned: when changed
|
||||
type: list
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
commands:
|
||||
description: The set of commands pushed to the remote device.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['command 1', 'command 2', 'command 3']
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.argspec.lacp_interfaces.lacp_interfaces import Lacp_interfacesArgs
|
||||
from ansible.module_utils.network.junos.config.lacp_interfaces.lacp_interfaces import Lacp_interfaces
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for module execution
|
||||
:returns: the result form module invocation
|
||||
"""
|
||||
required_if = [('state', 'merged', ('config',)),
|
||||
('state', 'replaced', ('config',)),
|
||||
('state', 'overridden', ('config',))]
|
||||
|
||||
module = AnsibleModule(argument_spec=Lacp_interfacesArgs.argument_spec,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
result = Lacp_interfaces(module).execute_module()
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,349 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The module file for junos_lag_interfaces
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_lag_interfaces
|
||||
version_added: 2.9
|
||||
short_description: Manage Link Aggregation on Juniper JUNOS devices.
|
||||
description: This module manages properties of Link Aggregation Group on Juniper JUNOS devices.
|
||||
author: Ganesh Nalawade (@ganeshrn)
|
||||
options:
|
||||
config:
|
||||
description: A list of link aggregation group configurations.
|
||||
type: list
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Name of the link aggregation group (LAG).
|
||||
type: str
|
||||
required: True
|
||||
mode:
|
||||
description:
|
||||
- LAG mode. A value of C(passive) will enable LACP in C(passive) mode that is it
|
||||
will respond to LACP packets and C(active) configures the link to initiate
|
||||
transmission of LACP packets.
|
||||
choices: ['active', 'passive']
|
||||
link_protection:
|
||||
description:
|
||||
- This boolean option indicates if link protection should be enabled for the LAG interface.
|
||||
If value is C(True) link protection is enabled on LAG and if value is C(False) link protection
|
||||
is disabled.
|
||||
type: bool
|
||||
members:
|
||||
description:
|
||||
- List of member interfaces of the link aggregation group. The value can be
|
||||
single interface or list of interfaces.
|
||||
type: list
|
||||
suboptions:
|
||||
member:
|
||||
description:
|
||||
- Name of the member interface.
|
||||
type: str
|
||||
link_type:
|
||||
description:
|
||||
- The value of this options configures the member link as either C(primary)
|
||||
or C(backup). Value C(primary) configures primary interface for link-protection mode
|
||||
and C(backup) configures backup interface for link-protection mode.
|
||||
choices: ['primary', 'backup']
|
||||
state:
|
||||
description:
|
||||
- The state of the configuration after module completion
|
||||
type: str
|
||||
choices:
|
||||
- merged
|
||||
- replaced
|
||||
- overridden
|
||||
- deleted
|
||||
default: merged
|
||||
requirements:
|
||||
- ncclient (>=v0.6.4)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 18.4R1.
|
||||
- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
"""
|
||||
EXAMPLES = """
|
||||
# Using merged
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Ansible configured interface 1";
|
||||
# ether-options {
|
||||
# 802.3ad ae0;
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "Ansible configured interface 2";
|
||||
# ether-options {
|
||||
# 802.3ad ae0;
|
||||
# }
|
||||
# }
|
||||
# ae0 {
|
||||
# description "lag interface";
|
||||
# }
|
||||
# ae1 {
|
||||
# description "lag interface 1";
|
||||
# }
|
||||
|
||||
- name: "Delete LAG attributes of given interfaces (Note: This won't delete the interface itself)"
|
||||
junos_lag_interfaces:
|
||||
config:
|
||||
- name: ae0
|
||||
- name: ae1
|
||||
state: deleted
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Ansible configured interface 1";
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "Ansible configured interface 2";
|
||||
# }
|
||||
|
||||
|
||||
# Using merged
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Ansible configured interface 1";
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "Ansible configured interface 2";
|
||||
# }
|
||||
|
||||
- name: Merge provided configuration with device configuration
|
||||
junos_lag_interfaces:
|
||||
config:
|
||||
- name: ae0
|
||||
members:
|
||||
- member: ge-0/0/1
|
||||
link_type: primary
|
||||
- member: ge-0/0/2
|
||||
link_type: backup
|
||||
state: merged
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Ansible configured interface 1";
|
||||
# ether-options {
|
||||
# 802.3ad {
|
||||
# ae0;
|
||||
# primary;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "Ansible configured interface 2";
|
||||
# ether-options {
|
||||
# 802.3ad {
|
||||
# ae0;
|
||||
# backup;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
# Using merged
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Ansible configured interface 1";
|
||||
# ether-options {
|
||||
# 802.3ad ae0;
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "Ansible configured interface 2";
|
||||
# ether-options {
|
||||
# 802.3ad ae0;
|
||||
# }
|
||||
# }
|
||||
# ae0 {
|
||||
# description "lag interface";
|
||||
# }
|
||||
# ae3 {
|
||||
# description "lag interface 3";
|
||||
# }
|
||||
|
||||
- name: Overrides all device LAG configuration with provided configuration
|
||||
junos_lag_interfaces:
|
||||
config:
|
||||
- name: ae0
|
||||
members:
|
||||
- member: ge-0/0/2
|
||||
- name: ae1
|
||||
members:
|
||||
- member: ge-0/0/1
|
||||
mode: passive
|
||||
state: overridden
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Ansible configured interface 1";
|
||||
# ether-options {
|
||||
# 802.3ad ae1;
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "Ansible configured interface 2";
|
||||
# ether-options {
|
||||
# 802.3ad ae0;
|
||||
# }
|
||||
# }
|
||||
# ae0 {
|
||||
# description "lag interface";
|
||||
# }
|
||||
# ae1 {
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# active;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
# Using merged
|
||||
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Ansible configured interface 1";
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "Ansible configured interface 2";
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# description "Ansible configured interface 3";
|
||||
# }
|
||||
|
||||
- name: Replace device LAG configuration with provided configuration
|
||||
junos_lag_interfaces:
|
||||
config:
|
||||
- name: ae0
|
||||
members:
|
||||
- member: ge-0/0/1
|
||||
mode: active
|
||||
state: replaced
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show interfaces
|
||||
# ge-0/0/1 {
|
||||
# description "Ansible configured interface 1";
|
||||
# ether-options {
|
||||
# 802.3ad ae0;
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/2 {
|
||||
# description "Ansible configured interface 2";
|
||||
# }
|
||||
# ae0 {
|
||||
# aggregated-ether-options {
|
||||
# lacp {
|
||||
# active;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ge-0/0/3 {
|
||||
# description "Ansible configured interface 3";
|
||||
# }
|
||||
|
||||
|
||||
"""
|
||||
RETURN = """
|
||||
before:
|
||||
description: The configuration as structured data prior to module invocation.
|
||||
returned: always
|
||||
type: list
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
after:
|
||||
description: The configuration as structured data after module completion.
|
||||
returned: when changed
|
||||
type: list
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
xml:
|
||||
description: The set of xml rpc payload pushed to the remote device.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['xml 1', 'xml 2', 'xml 3']
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs
|
||||
from ansible.module_utils.network.junos.config.lag_interfaces.lag_interfaces import Lag_interfaces
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for module execution
|
||||
|
||||
:returns: the result form module invocation
|
||||
"""
|
||||
required_if = [('state', 'merged', ('config',)),
|
||||
('state', 'replaced', ('config',)),
|
||||
('state', 'overridden', ('config',))]
|
||||
|
||||
module = AnsibleModule(argument_spec=Lag_interfacesArgs.argument_spec,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
result = Lag_interfaces(module).execute_module()
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,200 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The module file for junos_lldp_global
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_lldp_global
|
||||
version_added: 2.9
|
||||
short_description: Manage link layer discovery protocol (LLDP) attributes on Juniper JUNOS devices.
|
||||
description:
|
||||
- This module manages link layer discovery protocol (LLDP) attributes on Juniper JUNOS devices.
|
||||
author: Ganesh Nalawade (@ganeshrn)
|
||||
options:
|
||||
config:
|
||||
description: The list of link layer discovery protocol attribute configurations
|
||||
type: dict
|
||||
suboptions:
|
||||
enabled:
|
||||
description:
|
||||
- This argument is a boolean value to enabled or disable LLDP.
|
||||
type: bool
|
||||
interval:
|
||||
description:
|
||||
- Frequency at which LLDP advertisements are sent (in seconds).
|
||||
type: int
|
||||
address:
|
||||
description:
|
||||
- This argument sets the management address from LLDP.
|
||||
type: str
|
||||
transmit_delay:
|
||||
description:
|
||||
- Specify the number of seconds the device waits before sending
|
||||
advertisements to neighbors after a change is made in local system.
|
||||
type: int
|
||||
hold_multiplier:
|
||||
description:
|
||||
- Specify the number of seconds that LLDP information is held before it is
|
||||
discarded. The multiplier value is used in combination with the
|
||||
C(interval) value.
|
||||
type: int
|
||||
state:
|
||||
description:
|
||||
- The state of the configuration after module completion.
|
||||
type: str
|
||||
choices:
|
||||
- merged
|
||||
- replaced
|
||||
- deleted
|
||||
default: merged
|
||||
requirements:
|
||||
- ncclient (>=v0.6.4)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 18.4R1.
|
||||
- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
|
||||
"""
|
||||
EXAMPLES = """
|
||||
# Using merged
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# # show protocols lldp
|
||||
#
|
||||
- name: Merge provided configuration with device configuration
|
||||
junos_lldp_global:
|
||||
config:
|
||||
interval: 10000
|
||||
address: 10.1.1.1
|
||||
transmit_delay: 400
|
||||
hold_multiplier: 10
|
||||
state: merged
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show protocols lldp
|
||||
# management-address 10.1.1.1;
|
||||
# advertisement-interval 10000;
|
||||
# transmit-delay 400;
|
||||
# hold-multiplier 10;
|
||||
|
||||
# Using replaced
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show protocols lldp
|
||||
# management-address 10.1.1.1;
|
||||
# advertisement-interval 10000;
|
||||
# transmit-delay 400;
|
||||
# hold-multiplier 10;
|
||||
|
||||
- name: Replace provided configuration with device configuration
|
||||
junos_lldp_global:
|
||||
config:
|
||||
address: 20.2.2.2
|
||||
hold_multiplier: 30
|
||||
enabled: False
|
||||
state: replaced
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show protocols lldp
|
||||
# disable;
|
||||
# management-address 20.2.2.2;
|
||||
# hold-multiplier 30;
|
||||
|
||||
# Using deleted
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show protocols lldp
|
||||
# management-address 20.2.2.2;
|
||||
# hold-multiplier 30;
|
||||
|
||||
- name: Delete lldp configuration (this will by default remove all lldp configuration)
|
||||
junos_lldp_global:
|
||||
state: deleted
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# # show protocols lldp
|
||||
#
|
||||
"""
|
||||
RETURN = """
|
||||
before:
|
||||
description: The configuration as structured data prior to module invocation.
|
||||
returned: always
|
||||
type: dict
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
after:
|
||||
description: The configuration as structured data after module completion.
|
||||
returned: when changed
|
||||
type: dict
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
commands:
|
||||
description: The set of commands pushed to the remote device.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['xml 1', 'xml 2', 'xml 3']
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.argspec.lldp_global.lldp_global import Lldp_globalArgs
|
||||
from ansible.module_utils.network.junos.config.lldp_global.lldp_global import Lldp_global
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for module execution
|
||||
:returns: the result form module invocation
|
||||
"""
|
||||
required_if = [('state', 'merged', ('config',)),
|
||||
('state', 'replaced', ('config',))]
|
||||
|
||||
module = AnsibleModule(argument_spec=Lldp_globalArgs.argument_spec,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
result = Lldp_global(module).execute_module()
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,229 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The module file for junos_lldp_interfaces
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_lldp_interfaces
|
||||
version_added: 2.9
|
||||
short_description: Manage link layer discovery protocol (LLDP) attributes of interfaces on Juniper JUNOS devices
|
||||
description:
|
||||
- This module manages link layer discovery protocol (LLDP) attributes of interfaces on Juniper JUNOS devices.
|
||||
author: Ganesh Nalawade (@ganeshrn)
|
||||
options:
|
||||
config:
|
||||
description: The list of link layer discovery protocol interface attribute configurations
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
name:
|
||||
description:
|
||||
- Name of the interface LLDP needs to be configured on.
|
||||
type: str
|
||||
required: True
|
||||
enabled:
|
||||
description:
|
||||
- This is a boolean value to control disabling of LLDP on the interface C(name)
|
||||
type: bool
|
||||
state:
|
||||
description:
|
||||
- The state of the configuration after module completion.
|
||||
type: str
|
||||
choices:
|
||||
- merged
|
||||
- replaced
|
||||
- overridden
|
||||
- deleted
|
||||
default: merged
|
||||
"""
|
||||
EXAMPLES = """
|
||||
# Using merged
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# # show protocols lldp
|
||||
# management-address 10.1.1.1;
|
||||
# advertisement-interval 10000;
|
||||
|
||||
- name: Merge provided configuration with device configuration
|
||||
junos_lldp_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/1
|
||||
- name: ge-0/0/2
|
||||
enabled: False
|
||||
state: merged
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show protocols lldp
|
||||
# management-address 10.1.1.1;
|
||||
# advertisement-interval 10000;
|
||||
# interface ge-0/0/1;
|
||||
# interface ge-0/0/2 {
|
||||
# disable;
|
||||
# }
|
||||
|
||||
# Using replaced
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show protocols lldp
|
||||
# management-address 10.1.1.1;
|
||||
# advertisement-interval 10000;
|
||||
# interface ge-0/0/1;
|
||||
# interface ge-0/0/2 {
|
||||
# disable;
|
||||
# }
|
||||
|
||||
- name: Replace provided configuration with device configuration
|
||||
junos_lldp_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/2
|
||||
disable: False
|
||||
- name: ge-0/0/3
|
||||
enabled: False
|
||||
state: replaced
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show protocols lldp
|
||||
# management-address 10.1.1.1;
|
||||
# advertisement-interval 10000;
|
||||
# interface ge-0/0/1;
|
||||
# interface ge-0/0/2;
|
||||
# interface ge-0/0/3 {
|
||||
# disable;
|
||||
# }
|
||||
|
||||
# Using overridden
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show protocols lldp
|
||||
# management-address 10.1.1.1;
|
||||
# advertisement-interval 10000;
|
||||
# interface ge-0/0/1;
|
||||
# interface ge-0/0/2 {
|
||||
# disable;
|
||||
# }
|
||||
|
||||
- name: Override provided configuration with device configuration
|
||||
junos_lldp_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/2
|
||||
enabled: False
|
||||
state: overridden
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show protocols lldp
|
||||
# management-address 10.1.1.1;
|
||||
# advertisement-interval 10000;
|
||||
# interface ge-0/0/2 {
|
||||
# disable;
|
||||
# }
|
||||
|
||||
# Using deleted
|
||||
# Before state:
|
||||
# -------------
|
||||
# user@junos01# show protocols lldp
|
||||
# management-address 10.1.1.1;
|
||||
# advertisement-interval 10000;
|
||||
# interface ge-0/0/1;
|
||||
# interface ge-0/0/2;
|
||||
# interface ge-0/0/3 {
|
||||
# disable;
|
||||
# }
|
||||
- name: Delete lldp interface configuration (this will not delete other lldp configuration)
|
||||
junos_lldp_interfaces:
|
||||
config:
|
||||
- name: ge-0/0/1
|
||||
- name: ge-0/0/3
|
||||
state: deleted
|
||||
|
||||
# After state:
|
||||
# -------------
|
||||
# user@junos01# show protocols lldp
|
||||
# management-address 10.1.1.1;
|
||||
# advertisement-interval 10000;
|
||||
# interface ge-0/0/2;
|
||||
# interface ge-0/0/1;
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
before:
|
||||
description: The configuration as structured data prior to module invocation.
|
||||
returned: always
|
||||
type: list
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
after:
|
||||
description: The configuration as structured data after module completion.
|
||||
returned: when changed
|
||||
type: list
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
commands:
|
||||
description: The set of commands pushed to the remote device.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['xml 1', 'xml 2', 'xml 3']
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.argspec.lldp_interfaces.lldp_interfaces import Lldp_interfacesArgs
|
||||
from ansible.module_utils.network.junos.config.lldp_interfaces.lldp_interfaces import Lldp_interfaces
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for module execution
|
||||
:returns: the result form module invocation
|
||||
"""
|
||||
required_if = [('state', 'merged', ('config',)),
|
||||
('state', 'replaced', ('config',)),
|
||||
('state', 'overridden', ('config',))]
|
||||
|
||||
module = AnsibleModule(argument_spec=Lldp_interfacesArgs.argument_spec,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
result = Lldp_interfaces(module).execute_module()
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,284 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_logging
|
||||
version_added: "2.4"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
short_description: Manage logging on network devices
|
||||
description:
|
||||
- This module provides declarative management of logging
|
||||
on Juniper JUNOS devices.
|
||||
options:
|
||||
dest:
|
||||
description:
|
||||
- Destination of the logs.
|
||||
choices: ['console', 'host', 'file', 'user']
|
||||
name:
|
||||
description:
|
||||
- If value of C(dest) is I(file) it indicates file-name,
|
||||
for I(user) it indicates username and for I(host) indicates
|
||||
the host name to be notified.
|
||||
facility:
|
||||
description:
|
||||
- Set logging facility.
|
||||
level:
|
||||
description:
|
||||
- Set logging severity levels.
|
||||
aggregate:
|
||||
description: List of logging definitions.
|
||||
state:
|
||||
description:
|
||||
- State of the logging configuration.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
default: True
|
||||
type: bool
|
||||
rotate_frequency:
|
||||
description:
|
||||
- Rotate log frequency in minutes, this is applicable if value
|
||||
of I(dest) is C(file). The acceptable value is in range of 1 to 59.
|
||||
This controls the frequency after which log file is rotated.
|
||||
required: false
|
||||
size:
|
||||
description:
|
||||
- Size of the file in archive, this is applicable if value
|
||||
of I(dest) is C(file). The acceptable value is in range from 65536 to
|
||||
1073741824 bytes.
|
||||
required: false
|
||||
files:
|
||||
description:
|
||||
- Number of files to be archived, this is applicable if value
|
||||
of I(dest) is C(file). The acceptable value is in range from 1 to 1000.
|
||||
required: false
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure console logging
|
||||
junos_logging:
|
||||
dest: console
|
||||
facility: any
|
||||
level: critical
|
||||
|
||||
- name: remove console logging configuration
|
||||
junos_logging:
|
||||
dest: console
|
||||
state: absent
|
||||
|
||||
- name: configure file logging
|
||||
junos_logging:
|
||||
dest: file
|
||||
name: test
|
||||
facility: pfe
|
||||
level: error
|
||||
|
||||
- name: configure logging parameter
|
||||
junos_logging:
|
||||
files: 30
|
||||
size: 65536
|
||||
rotate_frequency: 10
|
||||
|
||||
- name: Configure file logging using aggregate
|
||||
junos_logging:
|
||||
dest: file
|
||||
aggregate:
|
||||
- name: test-1
|
||||
facility: pfe
|
||||
level: critical
|
||||
- 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 }
|
||||
- { dest: file, name: test-2, facility: kernel, level: emergency }
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
diff.prepared:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit system syslog]
|
||||
+ [edit system syslog]
|
||||
file interactive-commands { ... }
|
||||
+ file test {
|
||||
+ pfe critical;
|
||||
+ }
|
||||
"""
|
||||
import collections
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.common.utils import remove_default_spec
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, tostring
|
||||
from ansible.module_utils.network.junos.junos import load_config, map_params_to_obj, map_obj_to_ele, to_param_list
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def validate_files(value, module):
|
||||
if value and not 1 <= value <= 1000:
|
||||
module.fail_json(msg='files must be between 1 and 1000')
|
||||
|
||||
|
||||
def validate_size(value, module):
|
||||
if value and not 65536 <= value <= 1073741824:
|
||||
module.fail_json(msg='size must be between 65536 and 1073741824')
|
||||
|
||||
|
||||
def validate_rotate_frequency(value, module):
|
||||
if value and not 1 <= value <= 59:
|
||||
module.fail_json(msg='rotate_frequency must be between 1 and 59')
|
||||
|
||||
|
||||
def validate_param_values(module, obj, param=None):
|
||||
if not param:
|
||||
param = module.params
|
||||
for key in obj:
|
||||
# validate the param value (if validator func exists)
|
||||
validator = globals().get('validate_%s' % key)
|
||||
if callable(validator):
|
||||
validator(param.get(key), module)
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
element_spec = dict(
|
||||
dest=dict(choices=['console', 'host', 'file', 'user']),
|
||||
name=dict(),
|
||||
facility=dict(),
|
||||
level=dict(),
|
||||
rotate_frequency=dict(type='int'),
|
||||
size=dict(type='int'),
|
||||
files=dict(type='int'),
|
||||
src_addr=dict(),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
active=dict(default=True, type='bool')
|
||||
)
|
||||
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
|
||||
)
|
||||
|
||||
argument_spec.update(element_spec)
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
required_if = [('dest', 'host', ['name', 'facility', 'level']),
|
||||
('dest', 'file', ['name', 'facility', 'level']),
|
||||
('dest', 'user', ['name', 'facility', 'level']),
|
||||
('dest', 'console', ['facility', 'level'])]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False}
|
||||
|
||||
if warnings:
|
||||
result['warnings'] = warnings
|
||||
|
||||
params = to_param_list(module)
|
||||
|
||||
requests = list()
|
||||
for param in params:
|
||||
# if key doesn't exist in the item, get it from module.params
|
||||
for key in param:
|
||||
if param.get(key) is None:
|
||||
param[key] = module.params[key]
|
||||
|
||||
module._check_required_if(required_if, param)
|
||||
|
||||
item = param.copy()
|
||||
dest = item.get('dest')
|
||||
if dest == 'console' and item.get('name'):
|
||||
module.fail_json(msg="%s and %s are mutually exclusive" % ('console', 'name'))
|
||||
|
||||
top = 'system/syslog'
|
||||
is_facility_key = False
|
||||
field_top = None
|
||||
if dest:
|
||||
if dest == 'console':
|
||||
field_top = dest
|
||||
is_facility_key = True
|
||||
else:
|
||||
field_top = dest + '/contents'
|
||||
is_facility_key = False
|
||||
|
||||
param_to_xpath_map = collections.OrderedDict()
|
||||
param_to_xpath_map.update([
|
||||
('name', {'xpath': 'name', 'is_key': True, 'top': dest}),
|
||||
('facility', {'xpath': 'name', 'is_key': is_facility_key, 'top': field_top}),
|
||||
('size', {'xpath': 'size', 'leaf_only': True, 'is_key': True, 'top': 'archive'}),
|
||||
('files', {'xpath': 'files', 'leaf_only': True, 'is_key': True, 'top': 'archive'}),
|
||||
('rotate_frequency', {'xpath': 'log-rotate-frequency', 'leaf_only': True}),
|
||||
])
|
||||
|
||||
if item.get('level'):
|
||||
param_to_xpath_map['level'] = {'xpath': item.get('level'), 'tag_only': True, 'top': field_top}
|
||||
|
||||
validate_param_values(module, param_to_xpath_map, param=item)
|
||||
|
||||
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):
|
||||
for req in requests:
|
||||
diff = load_config(module, tostring(req), warnings, action='merge')
|
||||
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,193 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_netconf
|
||||
version_added: "2.1"
|
||||
author: "Peter Sprygada (@privateip)"
|
||||
short_description: Configures the Junos Netconf system service
|
||||
description:
|
||||
- This module provides an abstraction that enables and configures
|
||||
the netconf system service running on Junos devices. This module
|
||||
can be used to easily enable the Netconf API. Netconf provides
|
||||
a programmatic interface for working with configuration and state
|
||||
resources as defined in RFC 6242. If the C(netconf_port) is not
|
||||
mentioned in the task by default netconf will be enabled on port 830
|
||||
only.
|
||||
extends_documentation_fragment: junos
|
||||
options:
|
||||
netconf_port:
|
||||
description:
|
||||
- This argument specifies the port the netconf service should
|
||||
listen on for SSH connections. The default port as defined
|
||||
in RFC 6242 is 830.
|
||||
required: false
|
||||
default: 830
|
||||
aliases: ['listens_on']
|
||||
version_added: "2.2"
|
||||
state:
|
||||
description:
|
||||
- Specifies the state of the C(junos_netconf) resource on
|
||||
the remote device. If the I(state) argument is set to
|
||||
I(present) the netconf service will be configured. If the
|
||||
I(state) argument is set to I(absent) the netconf service
|
||||
will be removed from the configuration.
|
||||
required: false
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
notes:
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(network_cli). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
- If C(netconf_port) value is not mentioned in task by default it will be enabled on port 830 only.
|
||||
Although C(netconf_port) value can be from 1 through 65535, avoid configuring access on a port
|
||||
that is normally assigned for another service. This practice avoids potential resource conflicts.
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: enable netconf service on port 830
|
||||
junos_netconf:
|
||||
listens_on: 830
|
||||
state: present
|
||||
|
||||
- name: disable netconf service
|
||||
junos_netconf:
|
||||
state: absent
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: Returns the command sent to the remote device
|
||||
returned: when changed is True
|
||||
type: str
|
||||
sample: 'set system services netconf ssh port 830'
|
||||
"""
|
||||
import re
|
||||
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.connection import ConnectionError
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, get_connection
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def map_obj_to_commands(updates, module):
|
||||
want, have = updates
|
||||
commands = list()
|
||||
|
||||
if want['state'] == 'absent':
|
||||
if have['state'] == 'present':
|
||||
commands.append('delete system services netconf')
|
||||
else:
|
||||
if have['state'] == 'absent' or want['netconf_port'] != have.get('netconf_port'):
|
||||
commands.append(
|
||||
'set system services netconf ssh port %s' % want['netconf_port']
|
||||
)
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
def parse_port(config):
|
||||
match = re.search(r'port (\d+)', config)
|
||||
if match:
|
||||
return int(match.group(1))
|
||||
|
||||
|
||||
def map_config_to_obj(module):
|
||||
conn = get_connection(module)
|
||||
out = conn.get(command='show configuration system services netconf')
|
||||
if out is None:
|
||||
module.fail_json(msg='unable to retrieve current config')
|
||||
config = str(out).strip()
|
||||
|
||||
obj = {'state': 'absent'}
|
||||
if 'ssh' in config:
|
||||
obj.update({
|
||||
'state': 'present',
|
||||
'netconf_port': parse_port(config)
|
||||
})
|
||||
return obj
|
||||
|
||||
|
||||
def validate_netconf_port(value, module):
|
||||
if not 1 <= value <= 65535:
|
||||
module.fail_json(msg='netconf_port must be between 1 and 65535')
|
||||
|
||||
|
||||
def map_params_to_obj(module):
|
||||
obj = {
|
||||
'netconf_port': module.params['netconf_port'],
|
||||
'state': module.params['state']
|
||||
}
|
||||
|
||||
for key, value in iteritems(obj):
|
||||
# validate the param value (if validator func exists)
|
||||
validator = globals().get('validate_%s' % key)
|
||||
if callable(validator):
|
||||
validator(value, module)
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
def load_config(module, config, commit=False):
|
||||
conn = get_connection(module)
|
||||
try:
|
||||
resp = conn.edit_config(to_list(config) + ['top'], commit)
|
||||
except ConnectionError as exc:
|
||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
||||
|
||||
diff = resp.get('diff', '')
|
||||
return to_text(diff, errors='surrogate_then_replace').strip()
|
||||
|
||||
|
||||
def main():
|
||||
"""main entry point for module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
netconf_port=dict(type='int', default=830, aliases=['listens_on']),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
)
|
||||
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False, 'warnings': warnings}
|
||||
|
||||
want = map_params_to_obj(module)
|
||||
have = map_config_to_obj(module)
|
||||
|
||||
commands = map_obj_to_commands((want, have), module)
|
||||
result['commands'] = commands
|
||||
|
||||
if commands:
|
||||
commit = not module.check_mode
|
||||
diff = load_config(module, commands, commit=commit)
|
||||
if diff:
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
result['changed'] = True
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,224 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_package
|
||||
version_added: "2.1"
|
||||
author: "Peter Sprygada (@privateip)"
|
||||
short_description: Installs packages on remote devices running Junos
|
||||
description:
|
||||
- This module can install new and updated packages on remote
|
||||
devices running Junos. The module will compare the specified
|
||||
package with the one running on the remote device and install
|
||||
the specified version if there is a mismatch
|
||||
extends_documentation_fragment: junos
|
||||
options:
|
||||
src:
|
||||
description:
|
||||
- The I(src) argument specifies the path to the source package to be
|
||||
installed on the remote device in the advent of a version mismatch.
|
||||
The I(src) argument can be either a localized path or a full
|
||||
path to the package file to install.
|
||||
required: true
|
||||
aliases: ['package']
|
||||
version:
|
||||
description:
|
||||
- The I(version) argument can be used to explicitly specify the
|
||||
version of the package that should be installed on the remote
|
||||
device. If the I(version) argument is not specified, then
|
||||
the version is extracts from the I(src) filename.
|
||||
reboot:
|
||||
description:
|
||||
- In order for a package to take effect, the remote device must be
|
||||
restarted. When enabled, this argument will instruct the module
|
||||
to reboot the device once the updated package has been installed.
|
||||
If disabled or the remote package does not need to be changed,
|
||||
the device will not be started.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
no_copy:
|
||||
description:
|
||||
- The I(no_copy) argument is responsible for instructing the remote
|
||||
device on where to install the package from. When enabled, the
|
||||
package is transferred to the remote device prior to installing.
|
||||
type: bool
|
||||
default: 'no'
|
||||
validate:
|
||||
description:
|
||||
- The I(validate) argument is responsible for instructing the remote
|
||||
device to skip checking the current device configuration
|
||||
compatibility with the package being installed. When set to false
|
||||
validation is not performed.
|
||||
version_added: 2.5
|
||||
type: bool
|
||||
default: 'yes'
|
||||
force:
|
||||
description:
|
||||
- The I(force) argument instructs the module to bypass the package
|
||||
version check and install the packaged identified in I(src) on
|
||||
the remote device.
|
||||
type: bool
|
||||
default: 'no'
|
||||
force_host:
|
||||
description:
|
||||
- The I(force_host) argument controls the way software package or
|
||||
bundle is added on remote JUNOS host and is applicable
|
||||
for JUNOS QFX5100 device. If the value is set to C(True) it
|
||||
will ignore any warnings while adding the host software package or bundle.
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 2.8
|
||||
issu:
|
||||
description:
|
||||
- The I(issu) argument is a boolean flag when set to C(True) allows
|
||||
unified in-service software upgrade (ISSU) feature which enables
|
||||
you to upgrade between two different Junos OS releases with no
|
||||
disruption on the control plane and with minimal disruption of traffic.
|
||||
type: bool
|
||||
default: False
|
||||
version_added: 2.8
|
||||
ssh_private_key_file:
|
||||
description:
|
||||
- The C(ssh_private_key_file) argument is path to the SSH private key file.
|
||||
This can be used if you need to provide a private key rather than loading
|
||||
the key into the ssh-key-ring/environment
|
||||
type: path
|
||||
version_added: '2.10'
|
||||
ssh_config:
|
||||
description:
|
||||
- The C(ssh_config) argument is path to the SSH configuration file.
|
||||
This can be used to load SSH information from a configuration file.
|
||||
If this option is not given by default ~/.ssh/config is queried.
|
||||
type: path
|
||||
version_added: '2.10'
|
||||
requirements:
|
||||
- junos-eznc
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Works with C(local) connections only.
|
||||
- Since this module uses junos-eznc to establish connection with junos
|
||||
device the netconf configuration parameters needs to be passed
|
||||
using module options for example C(ssh_config) unlike other junos
|
||||
modules that uses C(netconf) connection type.
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# the required set of connection arguments have been purposely left off
|
||||
# the examples for brevity
|
||||
|
||||
- name: install local package on remote device
|
||||
junos_package:
|
||||
src: junos-vsrx-12.1X46-D10.2-domestic.tgz
|
||||
|
||||
- name: install local package on remote device without rebooting
|
||||
junos_package:
|
||||
src: junos-vsrx-12.1X46-D10.2-domestic.tgz
|
||||
reboot: no
|
||||
|
||||
- name: install local package on remote device with jumpost
|
||||
junos_package:
|
||||
src: junos-vsrx-12.1X46-D10.2-domestic.tgz
|
||||
ssh_config: /home/user/customsshconfig
|
||||
"""
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, get_device
|
||||
|
||||
try:
|
||||
from jnpr.junos.utils.sw import SW
|
||||
HAS_PYEZ = True
|
||||
except ImportError:
|
||||
HAS_PYEZ = False
|
||||
|
||||
|
||||
def install_package(module, device):
|
||||
junos = SW(device)
|
||||
package = module.params['src']
|
||||
no_copy = module.params['no_copy']
|
||||
validate = module.params['validate']
|
||||
force_host = module.params['force_host']
|
||||
issu = module.params['issu']
|
||||
|
||||
def progress_log(dev, report):
|
||||
module.log(report)
|
||||
|
||||
module.log('installing package')
|
||||
result = junos.install(package, progress=progress_log, no_copy=no_copy,
|
||||
validate=validate, force_host=force_host, issu=issu)
|
||||
|
||||
if not result:
|
||||
module.fail_json(msg='Unable to install package on device')
|
||||
|
||||
if module.params['reboot']:
|
||||
module.log('rebooting system')
|
||||
junos.reboot()
|
||||
|
||||
|
||||
def main():
|
||||
""" Main entry point for Ansible module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
src=dict(type='path', required=True, aliases=['package']),
|
||||
version=dict(),
|
||||
reboot=dict(type='bool', default=True),
|
||||
no_copy=dict(default=False, type='bool'),
|
||||
validate=dict(default=True, type='bool'),
|
||||
force=dict(type='bool', default=False),
|
||||
transport=dict(default='netconf', choices=['netconf']),
|
||||
force_host=dict(type='bool', default=False),
|
||||
issu=dict(type='bool', default=False),
|
||||
ssh_private_key_file=dict(type='path'),
|
||||
ssh_config=dict(type='path')
|
||||
)
|
||||
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
if module.params['provider'] is None:
|
||||
module.params['provider'] = {}
|
||||
|
||||
if not HAS_PYEZ:
|
||||
module.fail_json(
|
||||
msg='junos-eznc is required but does not appear to be installed. '
|
||||
'It can be installed using `pip install junos-eznc`'
|
||||
)
|
||||
|
||||
result = dict(changed=False)
|
||||
|
||||
do_upgrade = module.params['force'] or False
|
||||
|
||||
device = get_device(module)
|
||||
|
||||
if not module.params['force']:
|
||||
device.facts_refresh()
|
||||
has_ver = device.facts.get('version')
|
||||
wants_ver = module.params['version']
|
||||
do_upgrade = has_ver != wants_ver
|
||||
|
||||
if do_upgrade:
|
||||
if not module.check_mode:
|
||||
install_package(module, device)
|
||||
result['changed'] = True
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,232 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2019, Ansible by Red Hat, inc
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_ping
|
||||
short_description: Tests reachability using ping from devices running Juniper JUNOS
|
||||
description:
|
||||
- Tests reachability using ping from devices running Juniper JUNOS to a remote destination.
|
||||
- Tested against Junos (17.3R1.10)
|
||||
- For a general purpose network module, see the M(net_ping) module.
|
||||
- For Windows targets, use the M(win_ping) module instead.
|
||||
- For targets running Python, use the M(ping) module instead.
|
||||
author:
|
||||
- Nilashish Chakraborty (@NilashishC)
|
||||
version_added: '2.8'
|
||||
options:
|
||||
dest:
|
||||
description:
|
||||
- The IP Address or hostname (resolvable by the device) of the remote node.
|
||||
required: true
|
||||
count:
|
||||
description:
|
||||
- Number of packets to send to check reachability.
|
||||
type: int
|
||||
default: 5
|
||||
source:
|
||||
description:
|
||||
- The IP Address to use while sending the ping packet(s).
|
||||
interface:
|
||||
description:
|
||||
- The source interface to use while sending the ping packet(s).
|
||||
ttl:
|
||||
description:
|
||||
- The time-to-live value for the ICMP packet(s).
|
||||
type: int
|
||||
size:
|
||||
description:
|
||||
- Determines the size (in bytes) of the ping packet(s).
|
||||
type: int
|
||||
interval:
|
||||
description:
|
||||
- Determines the interval (in seconds) between consecutive pings.
|
||||
type: int
|
||||
state:
|
||||
description:
|
||||
- Determines if the expected result is success or fail.
|
||||
choices: [ absent, present ]
|
||||
default: present
|
||||
notes:
|
||||
- For a general purpose network module, see the M(net_ping) module.
|
||||
- For Windows targets, use the M(win_ping) module instead.
|
||||
- For targets running Python, use the M(ping) module instead.
|
||||
- This module works only with connection C(network_cli).
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Test reachability to 10.10.10.10
|
||||
junos_ping:
|
||||
dest: 10.10.10.10
|
||||
|
||||
- name: Test reachability to 10.20.20.20 using source and size set
|
||||
junos_ping:
|
||||
dest: 10.20.20.20
|
||||
size: 1024
|
||||
ttl: 128
|
||||
|
||||
- name: Test unreachability to 10.30.30.30 using interval
|
||||
junos_ping:
|
||||
dest: 10.30.30.30
|
||||
interval: 3
|
||||
state: absent
|
||||
|
||||
- name: Test reachability to 10.40.40.40 setting count and interface
|
||||
junos_ping:
|
||||
dest: 10.40.40.40
|
||||
interface: fxp0
|
||||
count: 20
|
||||
size: 512
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: List of commands sent.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ["ping 10.8.38.44 count 10 source 10.8.38.38 ttl 128"]
|
||||
packet_loss:
|
||||
description: Percentage of packets lost.
|
||||
returned: always
|
||||
type: str
|
||||
sample: "0%"
|
||||
packets_rx:
|
||||
description: Packets successfully received.
|
||||
returned: always
|
||||
type: int
|
||||
sample: 20
|
||||
packets_tx:
|
||||
description: Packets successfully transmitted.
|
||||
returned: always
|
||||
type: int
|
||||
sample: 20
|
||||
rtt:
|
||||
description: The round trip time (RTT) stats.
|
||||
returned: when ping succeeds
|
||||
type: dict
|
||||
sample: {"avg": 2, "max": 8, "min": 1, "stddev": 24}
|
||||
"""
|
||||
|
||||
import re
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, get_connection
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
count=dict(type="int", default=5),
|
||||
dest=dict(type="str", required=True),
|
||||
source=dict(),
|
||||
interface=dict(),
|
||||
ttl=dict(type='int'),
|
||||
size=dict(type='int'),
|
||||
interval=dict(type='int'),
|
||||
state=dict(type="str", choices=["absent", "present"], default="present"),
|
||||
)
|
||||
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
|
||||
count = module.params["count"]
|
||||
dest = module.params["dest"]
|
||||
source = module.params["source"]
|
||||
size = module.params["size"]
|
||||
ttl = module.params["ttl"]
|
||||
interval = module.params["interval"]
|
||||
interface = module.params['interface']
|
||||
warnings = list()
|
||||
|
||||
results = {'changed': False}
|
||||
if warnings:
|
||||
results["warnings"] = warnings
|
||||
|
||||
results["commands"] = build_ping(dest, count, size, interval, source, ttl, interface)
|
||||
conn = get_connection(module)
|
||||
|
||||
ping_results = conn.get(results["commands"])
|
||||
|
||||
rtt_info, rate_info = None, None
|
||||
for line in ping_results.split("\n"):
|
||||
if line.startswith('round-trip'):
|
||||
rtt_info = line
|
||||
if line.startswith('%s packets transmitted' % count):
|
||||
rate_info = line
|
||||
|
||||
if rtt_info:
|
||||
rtt = parse_rtt(rtt_info)
|
||||
for k, v in rtt.items():
|
||||
if rtt[k] is not None:
|
||||
rtt[k] = float(v)
|
||||
results["rtt"] = rtt
|
||||
|
||||
pkt_loss, rx, tx = parse_rate(rate_info)
|
||||
results["packet_loss"] = str(pkt_loss) + "%"
|
||||
results["packets_rx"] = int(rx)
|
||||
results["packets_tx"] = int(tx)
|
||||
|
||||
validate_results(module, pkt_loss, results)
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
def build_ping(dest, count, size=None, interval=None, source=None, ttl=None, interface=None):
|
||||
cmd = "ping {0} count {1}".format(dest, str(count))
|
||||
|
||||
if source:
|
||||
cmd += " source {0}".format(source)
|
||||
|
||||
if interface:
|
||||
cmd += " interface {0}".format(interface)
|
||||
|
||||
if ttl:
|
||||
cmd += " ttl {0}".format(str(ttl))
|
||||
|
||||
if size:
|
||||
cmd += " size {0}".format(str(size))
|
||||
|
||||
if interval:
|
||||
cmd += " interval {0}".format(str(interval))
|
||||
|
||||
return cmd
|
||||
|
||||
|
||||
def parse_rate(rate_info):
|
||||
rate_re = re.compile(
|
||||
r"(?P<tx>\d*) packets transmitted,(?:\s*)(?P<rx>\d*) packets received,(?:\s*)(?P<pkt_loss>\d*)% packet loss")
|
||||
rate = rate_re.match(rate_info)
|
||||
|
||||
return rate.group("pkt_loss"), rate.group("rx"), rate.group("tx")
|
||||
|
||||
|
||||
def parse_rtt(rtt_info):
|
||||
rtt_re = re.compile(
|
||||
r"round-trip (?:.*)=(?:\s*)(?P<min>\d+\.\d+).(?:\d*)/(?P<avg>\d+\.\d+).(?:\d*)/(?P<max>\d*\.\d*).(?:\d*)/(?P<stddev>\d*\.\d*)")
|
||||
rtt = rtt_re.match(rtt_info)
|
||||
|
||||
return rtt.groupdict()
|
||||
|
||||
|
||||
def validate_results(module, loss, results):
|
||||
state = module.params["state"]
|
||||
if state == "present" and int(loss) == 100:
|
||||
module.fail_json(msg="Ping failed unexpectedly", **results)
|
||||
elif state == "absent" and int(loss) < 100:
|
||||
module.fail_json(msg="Ping succeeded unexpectedly", **results)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,173 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_rpc
|
||||
version_added: "2.3"
|
||||
author: "Peter Sprygada (@privateip)"
|
||||
short_description: Runs an arbitrary RPC over NetConf on an Juniper JUNOS device
|
||||
description:
|
||||
- Sends a request to the remote device running JUNOS to execute the
|
||||
specified RPC using the NetConf transport. The reply is then
|
||||
returned to the playbook in the C(xml) key. If an alternate output
|
||||
format is requested, the reply is transformed to the requested output.
|
||||
extends_documentation_fragment: junos
|
||||
options:
|
||||
rpc:
|
||||
description:
|
||||
- The C(rpc) argument specifies the RPC call to send to the
|
||||
remote devices to be executed. The RPC Reply message is parsed
|
||||
and the contents are returned to the playbook.
|
||||
required: true
|
||||
args:
|
||||
description:
|
||||
- The C(args) argument provides a set of arguments for the RPC
|
||||
call and are encoded in the request message. This argument
|
||||
accepts a set of key=value arguments.
|
||||
attrs:
|
||||
description:
|
||||
- The C(attrs) arguments defines a list of attributes and their values
|
||||
to set for the RPC call. This accepts a dictionary of key-values.
|
||||
version_added: "2.5"
|
||||
output:
|
||||
description:
|
||||
- The C(output) argument specifies the desired output of the
|
||||
return data. This argument accepts one of C(xml), C(text),
|
||||
or C(json). For C(json), the JUNOS device must be running a
|
||||
version of software that supports native JSON output.
|
||||
default: xml
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: collect interface information using rpc
|
||||
junos_rpc:
|
||||
rpc: get-interface-information
|
||||
args:
|
||||
interface-name: em0
|
||||
media: True
|
||||
|
||||
- name: get system information
|
||||
junos_rpc:
|
||||
rpc: get-system-information
|
||||
|
||||
- name: load configuration
|
||||
junos_rpc:
|
||||
rpc: load-configuration
|
||||
attrs:
|
||||
action: override
|
||||
url: /tmp/config.conf
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
xml:
|
||||
description: The xml return string from the rpc request.
|
||||
returned: always
|
||||
type: str
|
||||
output:
|
||||
description: The rpc rely converted to the output format.
|
||||
returned: always
|
||||
type: str
|
||||
output_lines:
|
||||
description: The text output split into lines for readability.
|
||||
returned: always
|
||||
type: list
|
||||
"""
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.common.netconf import exec_rpc
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, tostring
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
try:
|
||||
from lxml.etree import Element, SubElement
|
||||
except ImportError:
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
|
||||
|
||||
def main():
|
||||
"""main entry point for Ansible module
|
||||
"""
|
||||
argument_spec = dict(
|
||||
rpc=dict(required=True),
|
||||
args=dict(type='dict'),
|
||||
attrs=dict(type='dict'),
|
||||
output=dict(default='xml', choices=['xml', 'json', 'text']),
|
||||
)
|
||||
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=False)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False, 'warnings': warnings}
|
||||
|
||||
rpc = str(module.params['rpc']).replace('_', '-')
|
||||
|
||||
if all((module.check_mode, not rpc.startswith('get'))):
|
||||
module.fail_json(msg='invalid rpc for running in check_mode')
|
||||
|
||||
args = module.params['args'] or {}
|
||||
attrs = module.params['attrs'] or {}
|
||||
|
||||
xattrs = {'format': module.params['output']}
|
||||
|
||||
for key, value in iteritems(attrs):
|
||||
xattrs.update({key: value})
|
||||
|
||||
element = Element(module.params['rpc'], xattrs)
|
||||
|
||||
for key, value in iteritems(args):
|
||||
key = str(key).replace('_', '-')
|
||||
if isinstance(value, list):
|
||||
for item in value:
|
||||
child = SubElement(element, key)
|
||||
if item is not True:
|
||||
child.text = item
|
||||
else:
|
||||
child = SubElement(element, key)
|
||||
if value is not True:
|
||||
child.text = value
|
||||
|
||||
reply = exec_rpc(module, tostring(element), ignore_warning=False)
|
||||
|
||||
result['xml'] = tostring(reply)
|
||||
|
||||
if module.params['output'] == 'text':
|
||||
data = reply.find('.//output')
|
||||
result['output'] = data.text.strip()
|
||||
result['output_lines'] = result['output'].split('\n')
|
||||
|
||||
elif module.params['output'] == 'json':
|
||||
result['output'] = module.from_json(reply.text.strip())
|
||||
|
||||
else:
|
||||
result['output'] = tostring(reply).split('\n')
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,180 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2018, Ansible by Red Hat, inc
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_scp
|
||||
version_added: "2.5"
|
||||
author: "Christian Giese (@GIC-de)"
|
||||
short_description: Transfer files from or to remote devices running Junos
|
||||
description:
|
||||
- This module transfers files via SCP from or to remote devices
|
||||
running Junos.
|
||||
extends_documentation_fragment: junos
|
||||
options:
|
||||
src:
|
||||
description:
|
||||
- The C(src) argument takes a single path, or a list of paths to be
|
||||
transferred. The argument C(recursive) must be C(true) to transfer
|
||||
directories.
|
||||
required: true
|
||||
dest:
|
||||
description:
|
||||
- The C(dest) argument specifies the path in which to receive the files.
|
||||
default: '.'
|
||||
recursive:
|
||||
description:
|
||||
- The C(recursive) argument enables recursive transfer of files and
|
||||
directories.
|
||||
type: bool
|
||||
default: 'no'
|
||||
remote_src:
|
||||
description:
|
||||
- The C(remote_src) argument enables the download of files (I(scp get)) from
|
||||
the remote device. The default behavior is to upload files (I(scp put))
|
||||
to the remote device.
|
||||
type: bool
|
||||
default: 'no'
|
||||
ssh_private_key_file:
|
||||
description:
|
||||
- The C(ssh_private_key_file) argument is path to the SSH private key file.
|
||||
This can be used if you need to provide a private key rather than loading
|
||||
the key into the ssh-key-ring/environment
|
||||
type: path
|
||||
version_added: '2.10'
|
||||
ssh_config:
|
||||
description:
|
||||
- The C(ssh_config) argument is path to the SSH configuration file.
|
||||
This can be used to load SSH information from a configuration file.
|
||||
If this option is not given by default ~/.ssh/config is queried.
|
||||
type: path
|
||||
version_added: '2.10'
|
||||
requirements:
|
||||
- junos-eznc
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vMX JUNOS version 17.3R1.10.
|
||||
- Works with C(local) connections only.
|
||||
- Since this module uses junos-eznc to establish connection with junos
|
||||
device the netconf configuration parameters needs to be passed
|
||||
using module options for example C(ssh_config) unlike other junos
|
||||
modules that uses C(netconf) connection type.
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# the required set of connection arguments have been purposely left off
|
||||
# the examples for brevity
|
||||
- name: upload local file to home directory on remote device
|
||||
junos_scp:
|
||||
src: test.tgz
|
||||
|
||||
- name: upload local file to tmp directory on remote device
|
||||
junos_scp:
|
||||
src: test.tgz
|
||||
dest: /tmp/
|
||||
|
||||
- name: download file from remote device
|
||||
junos_scp:
|
||||
src: test.tgz
|
||||
remote_src: true
|
||||
|
||||
- name: ssh config file path for jumphost config
|
||||
junos_scp:
|
||||
src: test.tgz
|
||||
remote_src: true
|
||||
ssh_config: /home/user/customsshconfig
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
changed:
|
||||
description: always true
|
||||
returned: always
|
||||
type: bool
|
||||
"""
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, get_device
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
from jnpr.junos.utils.scp import SCP
|
||||
HAS_PYEZ = True
|
||||
except ImportError:
|
||||
HAS_PYEZ = False
|
||||
|
||||
|
||||
def transfer_files(module, device):
|
||||
dest = module.params['dest']
|
||||
recursive = module.params['recursive']
|
||||
|
||||
with SCP(device) as scp:
|
||||
for src in module.params['src']:
|
||||
if module.params['remote_src']:
|
||||
scp.get(src.strip(), local_path=dest, recursive=recursive)
|
||||
else:
|
||||
scp.put(src.strip(), remote_path=dest, recursive=recursive)
|
||||
|
||||
|
||||
def main():
|
||||
""" Main entry point for Ansible module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
src=dict(type='list', required=True),
|
||||
dest=dict(type='path', required=False, default="."),
|
||||
recursive=dict(type='bool', default=False),
|
||||
remote_src=dict(type='bool', default=False),
|
||||
ssh_private_key_file=dict(type='path'),
|
||||
ssh_config=dict(type='path'),
|
||||
transport=dict(default='netconf', choices=['netconf'])
|
||||
)
|
||||
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
if module.params['provider'] is None:
|
||||
module.params['provider'] = {}
|
||||
|
||||
if not HAS_PYEZ:
|
||||
module.fail_json(
|
||||
msg='junos-eznc is required but does not appear to be installed. '
|
||||
'It can be installed using `pip install junos-eznc`'
|
||||
)
|
||||
|
||||
result = dict(changed=True)
|
||||
|
||||
if not module.check_mode:
|
||||
# open pyez connection and transfer files via SCP
|
||||
try:
|
||||
device = get_device(module)
|
||||
transfer_files(module, device)
|
||||
except Exception as ex:
|
||||
module.fail_json(
|
||||
msg=to_native(ex)
|
||||
)
|
||||
finally:
|
||||
try:
|
||||
# close pyez connection and ignore exceptions
|
||||
device.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,282 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The module file for junos_static_routes
|
||||
"""
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_static_routes
|
||||
version_added: '2.10'
|
||||
short_description: Manage static routes on Juniper JUNOS devices
|
||||
description: This module provides declarative management of static routes on Juniper JUNOS devices
|
||||
author: Daniel Mellado (@dmellado)
|
||||
requirements:
|
||||
- ncclient (>=v0.6.4)
|
||||
- xmltodict (>=0.12)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on the device being managed.
|
||||
- This module works with connection C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- Tested against JunOS v18.4R1
|
||||
options:
|
||||
config:
|
||||
description: A dictionary of static routes options
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
vrf:
|
||||
description:
|
||||
- Virtual Routing and Forwarding (VRF) name
|
||||
type: str
|
||||
address_families:
|
||||
description:
|
||||
- Address family to use for the static routes
|
||||
elements: dict
|
||||
type: list
|
||||
suboptions:
|
||||
afi:
|
||||
description:
|
||||
- afi to use for the static routes
|
||||
type: str
|
||||
required: true
|
||||
choices:
|
||||
- ipv4
|
||||
- ipv6
|
||||
routes:
|
||||
description:
|
||||
- Static route configuration
|
||||
elements: dict
|
||||
type: list
|
||||
suboptions:
|
||||
dest:
|
||||
description:
|
||||
- Static route destination including prefix
|
||||
type: str
|
||||
next_hop:
|
||||
elements: dict
|
||||
type: list
|
||||
description:
|
||||
- Next hop to destination
|
||||
suboptions:
|
||||
forward_router_address:
|
||||
description:
|
||||
- List of next hops
|
||||
type: str
|
||||
metric:
|
||||
description:
|
||||
- Metric value for the static route
|
||||
type: int
|
||||
state:
|
||||
description:
|
||||
- The state the configuration should be left in
|
||||
type: str
|
||||
choices:
|
||||
- merged
|
||||
- replaced
|
||||
- overridden
|
||||
- deleted
|
||||
default: merged
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
---
|
||||
# Using deleted
|
||||
|
||||
# Before state
|
||||
# ------------
|
||||
#
|
||||
# admin# show routing-options
|
||||
# static {
|
||||
# route 192.168.47.0/24 next-hop 172.16.1.2;
|
||||
# route 192.168.16.0/24 next-hop 172.16.1.2;
|
||||
# route 10.200.16.75/24 next-hop 10.200.16.2;
|
||||
# }
|
||||
|
||||
- name: Delete provided configuration (default operation is merge)
|
||||
junos_static_routes:
|
||||
config:
|
||||
- address_families:
|
||||
- afi: 'ipv4'
|
||||
routes:
|
||||
- dest: 10.200.16.75/24
|
||||
next_hop:
|
||||
- forward_router_address: 10.200.16.2
|
||||
state: deleted
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
#
|
||||
# admin# show routing-options
|
||||
# static {
|
||||
# route 192.168.47.0/24 next-hop 172.16.1.2;
|
||||
# route 192.168.16.0/24 next-hop 172.16.1.2;
|
||||
# }
|
||||
|
||||
# Using merged
|
||||
|
||||
# Before state
|
||||
# ------------
|
||||
#
|
||||
# admin# show routing-options
|
||||
# static {
|
||||
# route 192.168.47.0/24 next-hop 172.16.1.2;
|
||||
# route 192.168.16.0/24 next-hop 172.16.1.2;
|
||||
# }
|
||||
|
||||
- name: Merge provided configuration with device configuration (default operation is merge)
|
||||
junos_static_routes:
|
||||
config:
|
||||
- address_families:
|
||||
- afi: 'ipv4'
|
||||
routes:
|
||||
- dest: 10.200.16.75/24
|
||||
next_hop:
|
||||
- forward_router_address: 10.200.16.2
|
||||
state: merged
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
#
|
||||
# admin# show routing-options
|
||||
# static {
|
||||
# route 192.168.47.0/24 next-hop 172.16.1.2;
|
||||
# route 192.168.16.0/24 next-hop 172.16.1.2;
|
||||
# route 10.200.16.75/24 next-hop 10.200.16.2;
|
||||
# }
|
||||
|
||||
# Using overridden
|
||||
|
||||
# Before state
|
||||
# ------------
|
||||
#
|
||||
# admin# show routing-options
|
||||
# static {
|
||||
# route 192.168.47.0/24 next-hop 172.16.1.2;
|
||||
# route 192.168.16.0/24 next-hop 172.16.0.1;
|
||||
# }
|
||||
|
||||
- name: Override provided configuration with device configuration (default operation is merge)
|
||||
junos_static_routes:
|
||||
config:
|
||||
- address_families:
|
||||
- afi: 'ipv4'
|
||||
routes:
|
||||
- dest: 10.200.16.75/24
|
||||
next_hop:
|
||||
- forward_router_address: 10.200.16.2
|
||||
state: overridden
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
#
|
||||
# admin# show routing-options
|
||||
# static {
|
||||
# route 10.200.16.75/24 next-hop 10.200.16.2;
|
||||
# }
|
||||
|
||||
# Using replaced
|
||||
|
||||
# Before state
|
||||
# ------------
|
||||
#
|
||||
# admin# show routing-options
|
||||
# static {
|
||||
# route 192.168.47.0/24 next-hop 172.16.1.2;
|
||||
# route 192.168.16.0/24 next-hop 172.16.1.2;
|
||||
# }
|
||||
|
||||
- name: Replace provided configuration with device configuration (default operation is merge)
|
||||
junos_static_routes:
|
||||
config:
|
||||
- address_families:
|
||||
- afi: 'ipv4'
|
||||
routes:
|
||||
- dest: 192.168.47.0/24
|
||||
next_hop:
|
||||
- forward_router_address: 10.200.16.2
|
||||
state: replaced
|
||||
|
||||
# After state:
|
||||
# ------------
|
||||
#
|
||||
# admin# show routing-options
|
||||
# static {
|
||||
# route 192.168.47.0/24 next-hop 10.200.16.2;
|
||||
# route 192.168.16.0/24 next-hop 172.16.1.2;
|
||||
# }
|
||||
|
||||
|
||||
"""
|
||||
RETURN = """
|
||||
before:
|
||||
description: The configuration prior to the model invocation.
|
||||
returned: always
|
||||
type: str
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
after:
|
||||
description: The resulting configuration model invocation.
|
||||
returned: when changed
|
||||
type: str
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
commands:
|
||||
description: The set of commands pushed to the remote device.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['command 1', 'command 2', 'command 3']
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.argspec.static_routes.static_routes import Static_routesArgs
|
||||
from ansible.module_utils.network.junos.config.static_routes.static_routes import Static_routes
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for module execution
|
||||
|
||||
:returns: the result form module invocation
|
||||
"""
|
||||
module = AnsibleModule(argument_spec=Static_routesArgs.argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
result = Static_routes(module).execute_module()
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,190 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_system
|
||||
version_added: "2.4"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
short_description: Manage the system attributes on Juniper JUNOS devices
|
||||
description:
|
||||
- This module provides declarative management of node system attributes
|
||||
on Juniper JUNOS devices. It provides an option to configure host system
|
||||
parameters or remove those parameters from the device active
|
||||
configuration.
|
||||
options:
|
||||
hostname:
|
||||
description:
|
||||
- Configure the device hostname parameter. This option takes an ASCII string value.
|
||||
domain_name:
|
||||
description:
|
||||
- Configure the IP domain name
|
||||
on the remote device to the provided value. Value
|
||||
should be in the dotted name form and will be
|
||||
appended to the C(hostname) to create a fully-qualified
|
||||
domain name.
|
||||
domain_search:
|
||||
description:
|
||||
- Provides the list of domain suffixes to
|
||||
append to the hostname for the purpose of doing name resolution.
|
||||
This argument accepts a list of names and will be reconciled
|
||||
with the current active configuration on the running node.
|
||||
name_servers:
|
||||
description:
|
||||
- List of DNS name servers by IP address to use to perform name resolution
|
||||
lookups. This argument accepts either a list of DNS servers See
|
||||
examples.
|
||||
state:
|
||||
description:
|
||||
- State of the configuration
|
||||
values in the device's current active configuration. When set
|
||||
to I(present), the values should be configured in the device active
|
||||
configuration and when set to I(absent) the values should not be
|
||||
in the device active configuration
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
default: True
|
||||
type: bool
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure hostname and domain name
|
||||
junos_system:
|
||||
hostname: junos01
|
||||
domain_name: test.example.com
|
||||
domain-search:
|
||||
- ansible.com
|
||||
- redhat.com
|
||||
- juniper.com
|
||||
|
||||
- name: remove configuration
|
||||
junos_system:
|
||||
state: absent
|
||||
|
||||
- name: configure name servers
|
||||
junos_system:
|
||||
name_servers:
|
||||
- 8.8.8.8
|
||||
- 8.8.4.4
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
diff.prepared:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit system]
|
||||
+ host-name test;
|
||||
+ domain-name ansible.com;
|
||||
+ domain-search redhat.com;
|
||||
[edit system name-server]
|
||||
172.26.1.1 { ... }
|
||||
+ 8.8.8.8;
|
||||
"""
|
||||
import collections
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, tostring
|
||||
from ansible.module_utils.network.junos.junos import load_config, map_params_to_obj, map_obj_to_ele
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config
|
||||
|
||||
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():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
hostname=dict(),
|
||||
domain_name=dict(),
|
||||
domain_search=dict(type='list'),
|
||||
name_servers=dict(type='list'),
|
||||
state=dict(choices=['present', 'absent'], default='present'),
|
||||
active=dict(default=True, type='bool')
|
||||
)
|
||||
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
params = ['hostname', 'domain_name', 'domain_search', 'name_servers']
|
||||
required_if = [('state', 'present', params, True),
|
||||
('state', 'absent', params, True),
|
||||
('state', 'active', params, True),
|
||||
('state', 'suspend', params, True)]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False}
|
||||
|
||||
if warnings:
|
||||
result['warnings'] = warnings
|
||||
|
||||
top = 'system'
|
||||
|
||||
param_to_xpath_map = collections.OrderedDict()
|
||||
param_to_xpath_map.update([
|
||||
('hostname', {'xpath': 'host-name', 'leaf_only': True}),
|
||||
('domain_name', {'xpath': 'domain-name', 'leaf_only': True}),
|
||||
('domain_search', {'xpath': 'domain-search', 'leaf_only': True, 'value_req': True}),
|
||||
('name_servers', {'xpath': 'name-server/name', 'is_key': True})
|
||||
])
|
||||
|
||||
validate_param_values(module, param_to_xpath_map)
|
||||
|
||||
want = map_params_to_obj(module, param_to_xpath_map)
|
||||
ele = map_obj_to_ele(module, want, top)
|
||||
|
||||
with locked_config(module):
|
||||
diff = load_config(module, tostring(ele), warnings, action='merge')
|
||||
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,371 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_user
|
||||
version_added: "2.3"
|
||||
author: "Peter Sprygada (@privateip)"
|
||||
short_description: Manage local user accounts on Juniper JUNOS devices
|
||||
description:
|
||||
- This module manages locally configured user accounts on remote
|
||||
network devices running the JUNOS operating system. It provides
|
||||
a set of arguments for creating, removing and updating locally
|
||||
defined accounts
|
||||
extends_documentation_fragment: junos
|
||||
options:
|
||||
aggregate:
|
||||
description:
|
||||
- The C(aggregate) argument defines a list of users to be configured
|
||||
on the remote device. The list of users will be compared against
|
||||
the current users and only changes will be added or removed from
|
||||
the device configuration. This argument is mutually exclusive with
|
||||
the name argument.
|
||||
version_added: "2.4"
|
||||
aliases: ['users', 'collection']
|
||||
name:
|
||||
description:
|
||||
- The C(name) argument defines the username of the user to be created
|
||||
on the system. This argument must follow appropriate usernaming
|
||||
conventions for the target device running JUNOS. This argument is
|
||||
mutually exclusive with the C(aggregate) argument.
|
||||
full_name:
|
||||
description:
|
||||
- The C(full_name) argument provides the full name of the user
|
||||
account to be created on the remote device. This argument accepts
|
||||
any text string value.
|
||||
role:
|
||||
description:
|
||||
- The C(role) argument defines the role of the user account on the
|
||||
remote system. User accounts can have more than one role
|
||||
configured.
|
||||
choices: ['operator', 'read-only', 'super-user', 'unauthorized']
|
||||
sshkey:
|
||||
description:
|
||||
- The C(sshkey) argument defines the public SSH key to be configured
|
||||
for the user account on the remote system. This argument must
|
||||
be a valid SSH key
|
||||
encrypted_password:
|
||||
description:
|
||||
- The C(encrypted_password) argument set already hashed password
|
||||
for the user account on the remote system.
|
||||
version_added: "2.8"
|
||||
purge:
|
||||
description:
|
||||
- The C(purge) argument instructs the module to consider the
|
||||
users definition absolute. It will remove any previously configured
|
||||
users on the device with the exception of the current defined
|
||||
set of aggregate.
|
||||
type: bool
|
||||
default: 'no'
|
||||
state:
|
||||
description:
|
||||
- The C(state) argument configures the state of the user definitions
|
||||
as it relates to the device operational configuration. When set
|
||||
to I(present), the user should be configured in the device active
|
||||
configuration and when set to I(absent) the user should not be
|
||||
in the device active configuration
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
type: bool
|
||||
default: 'yes'
|
||||
version_added: "2.4"
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: create new user account
|
||||
junos_user:
|
||||
name: ansible
|
||||
role: super-user
|
||||
sshkey: "{{ lookup('file', '~/.ssh/ansible.pub') }}"
|
||||
state: present
|
||||
|
||||
- name: remove a user account
|
||||
junos_user:
|
||||
name: ansible
|
||||
state: absent
|
||||
|
||||
- name: remove all user accounts except ansible
|
||||
junos_user:
|
||||
aggregate:
|
||||
- name: ansible
|
||||
purge: yes
|
||||
|
||||
- name: set user password
|
||||
junos_user:
|
||||
name: ansible
|
||||
role: super-user
|
||||
encrypted_password: "{{ 'my-password' | password_hash('sha512') }}"
|
||||
state: present
|
||||
|
||||
- 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 = """
|
||||
diff.prepared:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit system login]
|
||||
+ user test-user {
|
||||
+ uid 2005;
|
||||
+ class read-only;
|
||||
+ }
|
||||
"""
|
||||
from functools import partial
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.connection import ConnectionError
|
||||
from ansible.module_utils.network.common.utils import remove_default_spec
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, get_connection, tostring
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes
|
||||
from ansible.module_utils.network.junos.junos import load_config, locked_config
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
try:
|
||||
from lxml.etree import Element, SubElement
|
||||
except ImportError:
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
|
||||
ROLES = ['operator', 'read-only', 'super-user', 'unauthorized']
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def handle_purge(module, want):
|
||||
want_users = [item['name'] for item in want]
|
||||
element = Element('system')
|
||||
login = SubElement(element, 'login')
|
||||
|
||||
conn = get_connection(module)
|
||||
try:
|
||||
reply = conn.execute_rpc(tostring(Element('get-configuration')), ignore_warning=False)
|
||||
except ConnectionError as exc:
|
||||
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
|
||||
|
||||
users = reply.xpath('configuration/system/login/user/name')
|
||||
if users:
|
||||
for item in users:
|
||||
name = item.text
|
||||
if name not in want_users and name != 'root':
|
||||
user = SubElement(login, 'user', {'operation': 'delete'})
|
||||
SubElement(user, 'name').text = name
|
||||
if element.xpath('/system/login/user/name'):
|
||||
return element
|
||||
|
||||
|
||||
def map_obj_to_ele(module, want):
|
||||
element = Element('system')
|
||||
login = SubElement(element, 'login')
|
||||
|
||||
for item in want:
|
||||
if item['state'] != 'present':
|
||||
if item['name'] == 'root':
|
||||
module.fail_json(msg="cannot delete the 'root' account.")
|
||||
operation = 'delete'
|
||||
else:
|
||||
operation = 'merge'
|
||||
|
||||
if item['name'] != 'root':
|
||||
user = SubElement(login, 'user', {'operation': operation})
|
||||
SubElement(user, 'name').text = item['name']
|
||||
else:
|
||||
user = auth = SubElement(element, 'root-authentication', {'operation': operation})
|
||||
|
||||
if operation == 'merge':
|
||||
if item['name'] == 'root' and (not item['active'] or item['role'] or item['full_name']):
|
||||
module.fail_json(msg="'root' account cannot be deactivated or be assigned a role and a full name")
|
||||
|
||||
if item['active']:
|
||||
user.set('active', 'active')
|
||||
else:
|
||||
user.set('inactive', 'inactive')
|
||||
|
||||
if item['role']:
|
||||
SubElement(user, 'class').text = item['role']
|
||||
|
||||
if item.get('full_name'):
|
||||
SubElement(user, 'full-name').text = item['full_name']
|
||||
|
||||
if item.get('sshkey'):
|
||||
auth = SubElement(user, 'authentication')
|
||||
if 'ssh-rsa' in item['sshkey']:
|
||||
ssh_rsa = SubElement(auth, 'ssh-rsa')
|
||||
elif 'ssh-dss' in item['sshkey']:
|
||||
ssh_rsa = SubElement(auth, 'ssh-dsa')
|
||||
elif 'ecdsa-sha2' in item['sshkey']:
|
||||
ssh_rsa = SubElement(auth, 'ssh-ecdsa')
|
||||
elif 'ssh-ed25519' in item['sshkey']:
|
||||
ssh_rsa = SubElement(auth, 'ssh-ed25519')
|
||||
SubElement(ssh_rsa, 'name').text = item['sshkey']
|
||||
|
||||
if item.get('encrypted_password'):
|
||||
auth = SubElement(user, 'authentication')
|
||||
SubElement(auth, 'encrypted-password').text = item['encrypted_password']
|
||||
|
||||
return element
|
||||
|
||||
|
||||
def get_param_value(key, item, module):
|
||||
# if key doesn't exist in the item, get it from module.params
|
||||
if not item.get(key):
|
||||
value = module.params[key]
|
||||
|
||||
# if key does exist, do a type check on it to validate it
|
||||
else:
|
||||
value_type = module.argument_spec[key].get('type', 'str')
|
||||
type_checker = module._CHECK_ARGUMENT_TYPES_DISPATCHER[value_type]
|
||||
type_checker(item[key])
|
||||
value = item[key]
|
||||
|
||||
# validate the param value (if validator func exists)
|
||||
validator = globals().get('validate_%s' % key)
|
||||
if all((value, validator)):
|
||||
validator(value, module)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def map_params_to_obj(module):
|
||||
aggregate = module.params['aggregate']
|
||||
if not aggregate:
|
||||
if not module.params['name'] and module.params['purge']:
|
||||
return list()
|
||||
elif not module.params['name']:
|
||||
module.fail_json(msg='missing required argument: name')
|
||||
else:
|
||||
collection = [{'name': module.params['name']}]
|
||||
else:
|
||||
collection = list()
|
||||
for item in aggregate:
|
||||
if not isinstance(item, dict):
|
||||
collection.append({'username': item})
|
||||
elif 'name' not in item:
|
||||
module.fail_json(msg='missing required argument: name')
|
||||
else:
|
||||
collection.append(item)
|
||||
|
||||
objects = list()
|
||||
|
||||
for item in collection:
|
||||
get_value = partial(get_param_value, item=item, module=module)
|
||||
item.update({
|
||||
'full_name': get_value('full_name'),
|
||||
'role': get_value('role'),
|
||||
'encrypted_password': get_value('encrypted_password'),
|
||||
'sshkey': get_value('sshkey'),
|
||||
'state': get_value('state'),
|
||||
'active': get_value('active')
|
||||
})
|
||||
|
||||
for key, value in iteritems(item):
|
||||
# validate the param value (if validator func exists)
|
||||
validator = globals().get('validate_%s' % key)
|
||||
if all((value, validator)):
|
||||
validator(value, module)
|
||||
|
||||
objects.append(item)
|
||||
|
||||
return objects
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
element_spec = dict(
|
||||
name=dict(),
|
||||
full_name=dict(),
|
||||
role=dict(choices=ROLES),
|
||||
encrypted_password=dict(no_log=True),
|
||||
sshkey=dict(),
|
||||
state=dict(choices=['present', 'absent'], default='present'),
|
||||
active=dict(type='bool', default=True)
|
||||
)
|
||||
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict', options=aggregate_spec, aliases=['collection', 'users']),
|
||||
purge=dict(default=False, type='bool')
|
||||
)
|
||||
|
||||
argument_spec.update(element_spec)
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
mutually_exclusive = [['aggregate', 'name']]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False, 'warnings': warnings}
|
||||
|
||||
want = map_params_to_obj(module)
|
||||
ele = map_obj_to_ele(module, want)
|
||||
|
||||
purge_request = None
|
||||
if module.params['purge']:
|
||||
purge_request = handle_purge(module, want)
|
||||
|
||||
with locked_config(module):
|
||||
if purge_request:
|
||||
load_config(module, tostring(purge_request), warnings, action='replace')
|
||||
diff = load_config(module, tostring(ele), warnings, action='merge')
|
||||
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,283 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
#############################################
|
||||
# WARNING #
|
||||
#############################################
|
||||
#
|
||||
# This file is auto generated by the resource
|
||||
# module builder playbook.
|
||||
#
|
||||
# Do not edit this file manually.
|
||||
#
|
||||
# Changes to this file will be over written
|
||||
# by the resource module builder.
|
||||
#
|
||||
# Changes should be made in the model used to
|
||||
# generate this file or in the resource module
|
||||
# builder template.
|
||||
#
|
||||
#############################################
|
||||
|
||||
"""
|
||||
The module file for junos_vlans
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_vlans
|
||||
version_added: 2.9
|
||||
short_description: Create and manage VLAN configurations on Junos OS
|
||||
description: This module creates and manages VLAN configurations on Junos OS.
|
||||
author: Daniel Mellado (@danielmellado)
|
||||
requirements:
|
||||
- ncclient (>=v0.6.4)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed
|
||||
- Tested against Junos OS 18.4R1
|
||||
- This module works with connection C(netconf). See L(the Junos OS
|
||||
Platform Options,../network/user_guide/platform_junos.html).
|
||||
options:
|
||||
config:
|
||||
description: A dictionary of Vlan options
|
||||
type: list
|
||||
elements: dict
|
||||
suboptions:
|
||||
vlan_id:
|
||||
description:
|
||||
- IEEE 802.1q VLAN identifier for VLAN (1..4094).
|
||||
type: int
|
||||
required: true
|
||||
name:
|
||||
description:
|
||||
- Name of VLAN.
|
||||
type: str
|
||||
required: true
|
||||
description:
|
||||
description:
|
||||
- Text description of VLANs
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- The state of the configuration after module completion.
|
||||
type: str
|
||||
choices:
|
||||
- merged
|
||||
- replaced
|
||||
- overridden
|
||||
- deleted
|
||||
default: merged
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
# Using merged
|
||||
#############
|
||||
|
||||
# Before State
|
||||
# ------------
|
||||
#
|
||||
# admin# show vlans
|
||||
# vlan-2 {
|
||||
# vlan-id 2;
|
||||
# }
|
||||
# vlan-3 {
|
||||
# vlan-id 3;
|
||||
# }
|
||||
|
||||
- name: Merge JUNOS vlan
|
||||
junos_vlans:
|
||||
config:
|
||||
- name: vlan-1
|
||||
vlan-id: 1
|
||||
state: merged
|
||||
|
||||
# After State
|
||||
# -----------
|
||||
#
|
||||
# admin# show vlans
|
||||
# vlan-1 {
|
||||
# vlan-id 1;
|
||||
# }
|
||||
# vlan-2 {
|
||||
# vlan-id 2;
|
||||
# }
|
||||
# vlan-3 {
|
||||
# vlan-id 3;
|
||||
# }
|
||||
|
||||
|
||||
# Using replaced
|
||||
################
|
||||
|
||||
# Before State
|
||||
# ------------
|
||||
#
|
||||
# admin# show vlans
|
||||
# vlan-1 {
|
||||
# vlan-id 1;
|
||||
# }
|
||||
# vlan-2 {
|
||||
# vlan-id 2;
|
||||
# }
|
||||
# vlan-3 {
|
||||
# vlan-id 3;
|
||||
# }
|
||||
|
||||
- name: Replace JUNOS vlan
|
||||
junos_vlans:
|
||||
config:
|
||||
- name: vlan-1
|
||||
vlan-id: 10
|
||||
- name: vlan-3
|
||||
vlan-id: 30
|
||||
state: replaced
|
||||
|
||||
# After State
|
||||
# -----------
|
||||
#
|
||||
# admin# show vlans
|
||||
# vlan-1 {
|
||||
# vlan-id 10;
|
||||
# }
|
||||
# vlan-2 {
|
||||
# vlan-id 2;
|
||||
# }
|
||||
# vlan-3 {
|
||||
# vlan-id 30;
|
||||
# }
|
||||
|
||||
|
||||
# Using overridden
|
||||
##################
|
||||
|
||||
# Before State
|
||||
# ------------
|
||||
#
|
||||
# admin# show vlans
|
||||
# vlan-1 {
|
||||
# vlan-id 1;
|
||||
# }
|
||||
# vlan-2 {
|
||||
# vlan-id 2;
|
||||
# }
|
||||
# vlan-3 {
|
||||
# vlan-id 3;
|
||||
# }
|
||||
|
||||
- name: Override JUNOS vlan
|
||||
junos_vlans:
|
||||
config:
|
||||
- name: vlan-4
|
||||
vlan-id: 100
|
||||
- name: vlan-2
|
||||
vlan-id: 200
|
||||
state: overridden
|
||||
|
||||
# After State
|
||||
# -----------
|
||||
#
|
||||
# admin# show vlans
|
||||
# vlan-2 {
|
||||
# vlan-id 200;
|
||||
# }
|
||||
# vlan-4 {
|
||||
# vlan-id 100;
|
||||
# }
|
||||
|
||||
|
||||
#Using deleted
|
||||
##############
|
||||
|
||||
# Before State
|
||||
# ------------
|
||||
#
|
||||
# admin# show vlans
|
||||
# vlan-1 {
|
||||
# vlan-id 1;
|
||||
# }
|
||||
# vlan-2 {
|
||||
# vlan-id 2;
|
||||
# }
|
||||
# vlan-3 {
|
||||
# vlan-id 3;
|
||||
# }
|
||||
|
||||
- name: Delete JUNOS vlan
|
||||
junos_vlans:
|
||||
config:
|
||||
- name: vlan-1
|
||||
state: deleted
|
||||
|
||||
# After State
|
||||
# -----------
|
||||
#
|
||||
# admin# show vlans
|
||||
# vlan-2 {
|
||||
# vlan-id 2;
|
||||
# }
|
||||
# vlan-3 {
|
||||
# vlan-id 3;
|
||||
# }
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
before:
|
||||
description: The configuration as structured data prior to module invocation.
|
||||
returned: always
|
||||
type: str
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
after:
|
||||
description: The configuration as structured data after module completion.
|
||||
returned: when changed
|
||||
type: str
|
||||
sample: >
|
||||
The configuration returned will always be in the same format
|
||||
of the parameters above.
|
||||
commands:
|
||||
description: The set of commands pushed to the remote device.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['xml 1', 'xml 2', 'xml 3']
|
||||
"""
|
||||
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.junos.argspec.vlans.vlans import VlansArgs
|
||||
from ansible.module_utils.network.junos.config.vlans.vlans import Vlans
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for module execution
|
||||
|
||||
:returns: the result form module invocation
|
||||
"""
|
||||
required_if = [('state', 'merged', ('config',)),
|
||||
('state', 'replaced', ('config',)),
|
||||
('state', 'overridden', ('config',))]
|
||||
|
||||
module = AnsibleModule(argument_spec=VlansArgs.argument_spec,
|
||||
required_if=required_if,
|
||||
supports_check_mode=True)
|
||||
|
||||
result = Vlans(module).execute_module()
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,271 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (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)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'network'}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: junos_vrf
|
||||
version_added: "2.4"
|
||||
author: "Ganesh Nalawade (@ganeshrn)"
|
||||
short_description: Manage the VRF definitions on Juniper JUNOS devices
|
||||
description:
|
||||
- This module provides declarative management of VRF definitions on
|
||||
Juniper JUNOS devices. It allows playbooks to manage individual or
|
||||
the entire VRF collection.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The name of the VRF definition to be managed on the remote IOS
|
||||
device. The VRF definition name is an ASCII string name used
|
||||
to uniquely identify the VRF. This argument is mutually exclusive
|
||||
with the C(aggregate) argument
|
||||
description:
|
||||
description:
|
||||
- Provides a short description of the VRF definition in the
|
||||
current active configuration. The VRF definition value accepts
|
||||
alphanumeric characters used to provide additional information
|
||||
about the VRF.
|
||||
rd:
|
||||
description:
|
||||
- The router-distinguisher value uniquely identifies the VRF to
|
||||
routing processes on the remote IOS system. The RD value takes
|
||||
the form of C(A:B) where C(A) and C(B) are both numeric values.
|
||||
interfaces:
|
||||
description:
|
||||
- Identifies the set of interfaces that
|
||||
should be configured in the VRF. Interfaces must be routed
|
||||
interfaces in order to be placed into a VRF.
|
||||
target:
|
||||
description:
|
||||
- It configures VRF target community configuration. The target value takes
|
||||
the form of C(target:A:B) where C(A) and C(B) are both numeric values.
|
||||
table_label:
|
||||
description:
|
||||
- Causes JUNOS to allocate a VPN label per VRF rather than per VPN FEC.
|
||||
This allows for forwarding of traffic to directly connected subnets, COS
|
||||
Egress filtering etc.
|
||||
type: bool
|
||||
aggregate:
|
||||
description:
|
||||
- The set of VRF definition objects to be configured on the remote
|
||||
JUNOS device. Ths list entries can either be the VRF name or a hash
|
||||
of VRF definitions and attributes. This argument is mutually
|
||||
exclusive with the C(name) argument.
|
||||
state:
|
||||
description:
|
||||
- Configures the state of the VRF definition
|
||||
as it relates to the device operational configuration. When set
|
||||
to I(present), the VRF should be configured in the device active
|
||||
configuration and when set to I(absent) the VRF should not be
|
||||
in the device active configuration
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
active:
|
||||
description:
|
||||
- Specifies whether or not the configuration is active or deactivated
|
||||
default: True
|
||||
type: bool
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
- This module requires the netconf system service be enabled on
|
||||
the remote device being managed.
|
||||
- Tested against vSRX JUNOS version 15.1X49-D15.4, vqfx-10000 JUNOS Version 15.1X53-D60.4.
|
||||
- Recommended connection is C(netconf). See L(the Junos OS Platform Options,../network/user_guide/platform_junos.html).
|
||||
- This module also works with C(local) connections for legacy playbooks.
|
||||
extends_documentation_fragment: junos
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Configure vrf configuration
|
||||
junos_vrf:
|
||||
name: test-1
|
||||
description: test-vrf-1
|
||||
interfaces:
|
||||
- ge-0/0/3
|
||||
- ge-0/0/2
|
||||
rd: 192.0.2.1:10
|
||||
target: target:65514:113
|
||||
state: present
|
||||
|
||||
- name: Remove vrf configuration
|
||||
junos_vrf:
|
||||
name: test-1
|
||||
description: test-vrf-1
|
||||
interfaces:
|
||||
- ge-0/0/3
|
||||
- ge-0/0/2
|
||||
rd: 192.0.2.1:10
|
||||
target: target:65514:113
|
||||
state: absent
|
||||
|
||||
- name: Deactivate vrf configuration
|
||||
junos_vrf:
|
||||
name: test-1
|
||||
description: test-vrf-1
|
||||
interfaces:
|
||||
- ge-0/0/3
|
||||
- ge-0/0/2
|
||||
rd: 192.0.2.1:10
|
||||
target: target:65514:113
|
||||
active: False
|
||||
|
||||
- name: Activate vrf configuration
|
||||
junos_vrf:
|
||||
name: test-1
|
||||
description: test-vrf-1
|
||||
interfaces:
|
||||
- ge-0/0/3
|
||||
- ge-0/0/2
|
||||
rd: 192.0.2.1:10
|
||||
target: target:65514:113
|
||||
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: 192.0.2.1:10
|
||||
target: target:65514:113
|
||||
- name: test-2
|
||||
description: test-vrf-2
|
||||
interfaces:
|
||||
- ge-0/0/4
|
||||
- ge-0/0/5
|
||||
rd: 192.0.2.2:10
|
||||
target: target:65515:114
|
||||
state: present
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
diff.prepared:
|
||||
description: Configuration difference before and after applying change.
|
||||
returned: when configuration is changed and diff option is enabled.
|
||||
type: str
|
||||
sample: >
|
||||
[edit routing-instances]
|
||||
+ test-1 {
|
||||
+ description test-vrf-1;
|
||||
+ instance-type vrf;
|
||||
+ interface ge-0/0/2.0;
|
||||
+ interface ge-0/0/3.0;
|
||||
+ route-distinguisher 192.0.2.1:10;
|
||||
+ vrf-target target:65514:113;
|
||||
+ }
|
||||
"""
|
||||
import collections
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.common.utils import remove_default_spec
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, tostring
|
||||
from ansible.module_utils.network.junos.junos import load_config, map_params_to_obj, map_obj_to_ele, to_param_list
|
||||
from ansible.module_utils.network.junos.junos import commit_configuration, discard_changes, locked_config
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
def main():
|
||||
""" main entry point for module execution
|
||||
"""
|
||||
element_spec = dict(
|
||||
name=dict(),
|
||||
description=dict(),
|
||||
rd=dict(type='list'),
|
||||
interfaces=dict(type='list'),
|
||||
target=dict(type='list'),
|
||||
state=dict(default='present', choices=['present', 'absent']),
|
||||
active=dict(default=True, type='bool'),
|
||||
table_label=dict(default=True, type='bool')
|
||||
)
|
||||
|
||||
aggregate_spec = deepcopy(element_spec)
|
||||
aggregate_spec['name'] = dict(required=True)
|
||||
|
||||
# remove default in aggregate spec, to handle common arguments
|
||||
remove_default_spec(aggregate_spec)
|
||||
|
||||
argument_spec = dict(
|
||||
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
|
||||
)
|
||||
|
||||
argument_spec.update(element_spec)
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
required_one_of = [['aggregate', 'name']]
|
||||
mutually_exclusive = [['aggregate', 'name']]
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_one_of=required_one_of,
|
||||
mutually_exclusive=mutually_exclusive)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False}
|
||||
|
||||
if warnings:
|
||||
result['warnings'] = warnings
|
||||
|
||||
top = 'routing-instances/instance'
|
||||
|
||||
param_to_xpath_map = collections.OrderedDict()
|
||||
param_to_xpath_map.update([
|
||||
('name', {'xpath': 'name', 'is_key': True}),
|
||||
('description', 'description'),
|
||||
('type', 'instance-type'),
|
||||
('rd', 'route-distinguisher/rd-type'),
|
||||
('interfaces', 'interface/name'),
|
||||
('target', 'vrf-target/community'),
|
||||
('table_label', {'xpath': 'vrf-table-label', 'tag_only': True}),
|
||||
])
|
||||
|
||||
params = to_param_list(module)
|
||||
requests = list()
|
||||
|
||||
for param in params:
|
||||
# if key doesn't exist in the item, get it from module.params
|
||||
for key in param:
|
||||
if param.get(key) is None:
|
||||
param[key] = module.params[key]
|
||||
|
||||
item = param.copy()
|
||||
item['type'] = 'vrf'
|
||||
|
||||
want = map_params_to_obj(module, param_to_xpath_map, param=item)
|
||||
requests.append(map_obj_to_ele(module, want, top, param=item))
|
||||
|
||||
with locked_config(module):
|
||||
for req in requests:
|
||||
diff = load_config(module, tostring(req), warnings, action='merge')
|
||||
|
||||
commit = not module.check_mode
|
||||
if diff:
|
||||
if commit:
|
||||
commit_configuration(module)
|
||||
else:
|
||||
discard_changes(module)
|
||||
result['changed'] = True
|
||||
|
||||
if module._diff:
|
||||
result['diff'] = {'prepared': diff}
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,115 +0,0 @@
|
||||
#
|
||||
# (c) 2016 Red Hat Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# 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/>.
|
||||
#
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import sys
|
||||
import copy
|
||||
|
||||
from ansible.module_utils.network.common.utils import load_provider
|
||||
from ansible.module_utils.network.junos.junos import junos_provider_spec
|
||||
from ansible.plugins.action.network import ActionModule as ActionNetworkModule
|
||||
from ansible.utils.display import Display
|
||||
|
||||
display = Display()
|
||||
|
||||
CLI_SUPPORTED_MODULES = ['junos_netconf', 'junos_ping', 'junos_command']
|
||||
|
||||
|
||||
class ActionModule(ActionNetworkModule):
|
||||
|
||||
def run(self, tmp=None, task_vars=None):
|
||||
del tmp # tmp no longer has any effect
|
||||
|
||||
module_name = self._task.action.split('.')[-1]
|
||||
self._config_module = True if module_name == 'junos_config' else False
|
||||
persistent_connection = self._play_context.connection.split('.')[-1]
|
||||
warnings = []
|
||||
|
||||
if self._play_context.connection == 'local':
|
||||
provider = load_provider(junos_provider_spec, self._task.args)
|
||||
pc = copy.deepcopy(self._play_context)
|
||||
pc.network_os = 'junipernetworks.junos.junos'
|
||||
pc.remote_addr = provider['host'] or self._play_context.remote_addr
|
||||
|
||||
if provider['transport'] == 'cli' and module_name not in CLI_SUPPORTED_MODULES:
|
||||
return {'failed': True, 'msg': "Transport type '%s' is not valid for '%s' module. "
|
||||
"Please see https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html"
|
||||
% (provider['transport'], module_name)}
|
||||
|
||||
if module_name == 'junos_netconf' or (provider['transport'] == 'cli' and module_name == 'junos_command'):
|
||||
pc.connection = 'ansible.netcommon.network_cli'
|
||||
pc.port = int(provider['port'] or self._play_context.port or 22)
|
||||
else:
|
||||
pc.connection = 'ansible.netcommon.netconf'
|
||||
pc.port = int(provider['port'] or self._play_context.port or 830)
|
||||
|
||||
pc.remote_user = provider['username'] or self._play_context.connection_user
|
||||
pc.password = provider['password'] or self._play_context.password
|
||||
pc.private_key_file = provider['ssh_keyfile'] or self._play_context.private_key_file
|
||||
|
||||
connection = self._shared_loader_obj.connection_loader.get('ansible.netcommon.persistent', pc, sys.stdin,
|
||||
task_uuid=self._task._uuid)
|
||||
|
||||
# TODO: Remove below code after ansible minimal is cut out
|
||||
if connection is None:
|
||||
pc.network_os = 'junos'
|
||||
if pc.connection.split('.')[-1] == 'netconf':
|
||||
pc.connection = 'netconf'
|
||||
else:
|
||||
pc.connection = 'network_cli'
|
||||
|
||||
connection = self._shared_loader_obj.connection_loader.get('persistent', pc, sys.stdin, task_uuid=self._task._uuid)
|
||||
|
||||
display.vvv('using connection plugin %s (was local)' % pc.connection, pc.remote_addr)
|
||||
|
||||
command_timeout = int(provider['timeout']) if provider['timeout'] else connection.get_option('persistent_command_timeout')
|
||||
connection.set_options(direct={'persistent_command_timeout': command_timeout})
|
||||
|
||||
socket_path = connection.run()
|
||||
display.vvvv('socket_path: %s' % socket_path, pc.remote_addr)
|
||||
if not socket_path:
|
||||
return {'failed': True,
|
||||
'msg': 'unable to open shell. Please see: ' +
|
||||
'https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell'}
|
||||
|
||||
task_vars['ansible_socket'] = socket_path
|
||||
warnings.append(['connection local support for this module is deprecated and will be removed in version 2.14, use connection %s' % pc.connection])
|
||||
elif persistent_connection in ('netconf', 'network_cli'):
|
||||
provider = self._task.args.get('provider', {})
|
||||
if any(provider.values()):
|
||||
# for legacy reasons provider value is required for junos_facts(optional) and junos_package
|
||||
# modules as it uses junos_eznc library to connect to remote host
|
||||
if not (module_name == 'junos_facts' or module_name == 'junos_package' or module_name == 'junos_scp'):
|
||||
display.warning('provider is unnecessary when using %s and will be ignored' % self._play_context.connection)
|
||||
del self._task.args['provider']
|
||||
|
||||
if (persistent_connection == 'network_cli' and module_name not in CLI_SUPPORTED_MODULES) or \
|
||||
(persistent_connection == 'netconf' and module_name in CLI_SUPPORTED_MODULES[0:2]):
|
||||
return {'failed': True, 'msg': "Connection type '%s' is not valid for '%s' module. "
|
||||
"Please see https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html"
|
||||
% (self._play_context.connection, module_name)}
|
||||
|
||||
result = super(ActionModule, self).run(task_vars=task_vars)
|
||||
if warnings:
|
||||
if 'warnings' in result:
|
||||
result['warnings'].extend(warnings)
|
||||
else:
|
||||
result['warnings'] = warnings
|
||||
return result
|
@ -1,270 +0,0 @@
|
||||
#
|
||||
# (c) 2017 Red Hat Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# 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/>.
|
||||
#
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
author: Ansible Networking Team
|
||||
cliconf: junos
|
||||
short_description: Use junos cliconf to run command on Juniper Junos OS platform
|
||||
description:
|
||||
- This junos plugin provides low level abstraction apis for
|
||||
sending and receiving CLI commands from Juniper Junos OS network devices.
|
||||
version_added: "2.4"
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
|
||||
from itertools import chain
|
||||
from functools import wraps
|
||||
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.common._collections_compat import Mapping
|
||||
from ansible.module_utils.network.common.utils import to_list
|
||||
from ansible.plugins.cliconf import CliconfBase
|
||||
|
||||
|
||||
def configure(func):
|
||||
@wraps(func)
|
||||
def wrapped(self, *args, **kwargs):
|
||||
prompt = self._connection.get_prompt()
|
||||
if not to_text(prompt, errors='surrogate_or_strict').strip().endswith('#'):
|
||||
self.send_command('configure')
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapped
|
||||
|
||||
|
||||
class Cliconf(CliconfBase):
|
||||
|
||||
def get_text(self, ele, tag):
|
||||
try:
|
||||
return to_text(ele.find(tag).text, errors='surrogate_then_replace').strip()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def get_device_info(self):
|
||||
device_info = dict()
|
||||
device_info['network_os'] = 'junos'
|
||||
|
||||
reply = self.get(command='show version')
|
||||
data = to_text(reply, errors='surrogate_or_strict').strip()
|
||||
|
||||
match = re.search(r'Junos: (\S+)', data)
|
||||
if match:
|
||||
device_info['network_os_version'] = match.group(1)
|
||||
|
||||
match = re.search(r'Model: (\S+)', data, re.M)
|
||||
if match:
|
||||
device_info['network_os_model'] = match.group(1)
|
||||
|
||||
match = re.search(r'Hostname: (\S+)', data, re.M)
|
||||
if match:
|
||||
device_info['network_os_hostname'] = match.group(1)
|
||||
return device_info
|
||||
|
||||
def get_config(self, source='running', format='text', flags=None):
|
||||
if source != 'running':
|
||||
raise ValueError("fetching configuration from %s is not supported" % source)
|
||||
|
||||
options_values = self.get_option_values()
|
||||
if format not in options_values['format']:
|
||||
raise ValueError("'format' value %s is invalid. Valid values are %s" % (format, ','.join(options_values['format'])))
|
||||
|
||||
if format == 'text':
|
||||
cmd = 'show configuration'
|
||||
else:
|
||||
cmd = 'show configuration | display %s' % format
|
||||
|
||||
cmd += ' '.join(to_list(flags))
|
||||
cmd = cmd.strip()
|
||||
return self.send_command(cmd)
|
||||
|
||||
@configure
|
||||
def edit_config(self, candidate=None, commit=True, replace=None, comment=None):
|
||||
|
||||
operations = self.get_device_operations()
|
||||
self.check_edit_config_capability(operations, candidate, commit, replace, comment)
|
||||
|
||||
resp = {}
|
||||
results = []
|
||||
requests = []
|
||||
|
||||
if replace:
|
||||
candidate = 'load override {0}'.format(replace)
|
||||
|
||||
for line in to_list(candidate):
|
||||
if not isinstance(line, Mapping):
|
||||
line = {'command': line}
|
||||
cmd = line['command']
|
||||
try:
|
||||
results.append(self.send_command(**line))
|
||||
except AnsibleConnectionFailure as exc:
|
||||
if "error: commit failed" in exc.message:
|
||||
self.discard_changes()
|
||||
raise
|
||||
requests.append(cmd)
|
||||
|
||||
diff = self.compare_configuration()
|
||||
if diff:
|
||||
resp['diff'] = diff
|
||||
|
||||
if commit:
|
||||
self.commit(comment=comment)
|
||||
else:
|
||||
self.discard_changes()
|
||||
|
||||
else:
|
||||
self.send_command('top')
|
||||
self.discard_changes()
|
||||
|
||||
resp['request'] = requests
|
||||
resp['response'] = results
|
||||
return resp
|
||||
|
||||
def get(self, command, prompt=None, answer=None, sendonly=False, output=None, newline=True, check_all=False):
|
||||
if output:
|
||||
command = self._get_command_with_output(command, output)
|
||||
return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, newline=newline, check_all=check_all)
|
||||
|
||||
@configure
|
||||
def commit(self, comment=None, confirmed=False, at_time=None, synchronize=False):
|
||||
"""
|
||||
Execute commit command on remote device.
|
||||
:param comment: Comment to be associated with commit
|
||||
:param confirmed: Boolean flag to indicate if the previous commit should confirmed
|
||||
:param at_time: Time at which to activate configuration changes
|
||||
:param synchronize: Boolean flag to indicate if commit should synchronize on remote peers
|
||||
:return: Command response received from device
|
||||
"""
|
||||
command = 'commit'
|
||||
if comment:
|
||||
command += ' comment {0}'.format(comment)
|
||||
if confirmed:
|
||||
command += ' confirmed'
|
||||
if at_time:
|
||||
command += ' {0}'.format(at_time)
|
||||
if synchronize:
|
||||
command += ' peers-synchronize'
|
||||
|
||||
command += ' and-quit'
|
||||
|
||||
try:
|
||||
response = self.send_command(command)
|
||||
except AnsibleConnectionFailure:
|
||||
self.discard_changes()
|
||||
raise
|
||||
|
||||
return response
|
||||
|
||||
@configure
|
||||
def discard_changes(self):
|
||||
command = 'rollback 0'
|
||||
for cmd in chain(to_list(command), ['exit']):
|
||||
self.send_command(cmd)
|
||||
|
||||
@configure
|
||||
def validate(self):
|
||||
return self.send_command('commit check')
|
||||
|
||||
@configure
|
||||
def compare_configuration(self, rollback_id=None):
|
||||
command = 'show | compare'
|
||||
if rollback_id is not None:
|
||||
command += ' rollback %s' % int(rollback_id)
|
||||
resp = self.send_command(command)
|
||||
|
||||
r = resp.splitlines()
|
||||
if len(r) == 1 and '[edit]' in r[0] or len(r) == 4 and r[1].startswith('- version'):
|
||||
resp = ''
|
||||
|
||||
return resp
|
||||
|
||||
@configure
|
||||
def rollback(self, rollback_id, commit=True):
|
||||
resp = {}
|
||||
self.send_command('rollback %s' % int(rollback_id))
|
||||
resp['diff'] = self.compare_configuration()
|
||||
if commit:
|
||||
self.commit()
|
||||
else:
|
||||
self.discard_changes()
|
||||
return resp
|
||||
|
||||
def get_diff(self, rollback_id=None):
|
||||
diff = {'config_diff': None}
|
||||
response = self.compare_configuration(rollback_id=rollback_id)
|
||||
if response:
|
||||
diff['config_diff'] = response
|
||||
return diff
|
||||
|
||||
def get_device_operations(self):
|
||||
return {
|
||||
'supports_diff_replace': False,
|
||||
'supports_commit': True,
|
||||
'supports_rollback': True,
|
||||
'supports_defaults': False,
|
||||
'supports_onbox_diff': True,
|
||||
'supports_commit_comment': True,
|
||||
'supports_multiline_delimiter': False,
|
||||
'supports_diff_match': False,
|
||||
'supports_diff_ignore_lines': False,
|
||||
'supports_generate_diff': False,
|
||||
'supports_replace': True
|
||||
}
|
||||
|
||||
def get_option_values(self):
|
||||
return {
|
||||
'format': ['text', 'set', 'xml', 'json'],
|
||||
'diff_match': [],
|
||||
'diff_replace': [],
|
||||
'output': ['text', 'set', 'xml', 'json']
|
||||
}
|
||||
|
||||
def get_capabilities(self):
|
||||
result = super(Cliconf, self).get_capabilities()
|
||||
result['rpc'] += ['commit', 'discard_changes', 'run_commands', 'compare_configuration', 'validate', 'get_diff']
|
||||
result['device_operations'] = self.get_device_operations()
|
||||
result.update(self.get_option_values())
|
||||
return json.dumps(result)
|
||||
|
||||
def set_cli_prompt_context(self):
|
||||
"""
|
||||
Make sure we are in the operational cli mode
|
||||
:return: None
|
||||
"""
|
||||
if self._connection.connected:
|
||||
self._update_cli_prompt_context(config_context='#')
|
||||
|
||||
def _get_command_with_output(self, command, output):
|
||||
options_values = self.get_option_values()
|
||||
if output not in options_values['output']:
|
||||
raise ValueError("'output' value %s is invalid. Valid values are %s" % (output, ','.join(options_values['output'])))
|
||||
|
||||
if output == 'json' and not command.endswith('| display json'):
|
||||
cmd = '%s | display json' % command
|
||||
elif output == 'xml' and not command.endswith('| display xml'):
|
||||
cmd = '%s | display xml' % command
|
||||
elif output == 'text' and (command.endswith('| display json') or command.endswith('| display xml')):
|
||||
cmd = command.rsplit('|', 1)[0]
|
||||
else:
|
||||
cmd = command
|
||||
return cmd
|
@ -1,69 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2015, Peter Sprygada <psprygada@ansible.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
# Standard files documentation fragment
|
||||
DOCUMENTATION = r'''
|
||||
options:
|
||||
provider:
|
||||
description:
|
||||
- B(Deprecated)
|
||||
- "Starting with Ansible 2.5 we recommend using C(connection: network_cli) or C(connection: netconf)."
|
||||
- For more information please see the L(Junos OS Platform Options guide, ../network/user_guide/platform_junos.html).
|
||||
- HORIZONTALLINE
|
||||
- A dict object containing connection details.
|
||||
type: dict
|
||||
suboptions:
|
||||
host:
|
||||
description:
|
||||
- Specifies the DNS host name or address for connecting to the remote
|
||||
device over the specified transport. The value of host is used as
|
||||
the destination address for the transport.
|
||||
type: str
|
||||
required: true
|
||||
port:
|
||||
description:
|
||||
- Specifies the port to use when building the connection to the remote
|
||||
device. The port value will default to the well known SSH port
|
||||
of 22 (for C(transport=cli)) or port 830 (for C(transport=netconf))
|
||||
device.
|
||||
type: int
|
||||
default: 22
|
||||
username:
|
||||
description:
|
||||
- Configures the username to use to authenticate the connection to
|
||||
the remote device. This value is used to authenticate
|
||||
the SSH session. If the value is not specified in the task, the
|
||||
value of environment variable C(ANSIBLE_NET_USERNAME) will be used instead.
|
||||
type: str
|
||||
password:
|
||||
description:
|
||||
- Specifies the password to use to authenticate the connection to
|
||||
the remote device. This value is used to authenticate
|
||||
the SSH session. If the value is not specified in the task, the
|
||||
value of environment variable C(ANSIBLE_NET_PASSWORD) will be used instead.
|
||||
type: str
|
||||
timeout:
|
||||
description:
|
||||
- Specifies the timeout in seconds for communicating with the network device
|
||||
for either connecting or sending commands. If the timeout is
|
||||
exceeded before the operation is completed, the module will error.
|
||||
type: int
|
||||
default: 10
|
||||
ssh_keyfile:
|
||||
description:
|
||||
- Specifies the SSH key to use to authenticate the connection to
|
||||
the remote device. This value is the path to the key
|
||||
used to authenticate the SSH session. If the value is not specified in
|
||||
the task, the value of environment variable C(ANSIBLE_NET_SSH_KEYFILE)
|
||||
will be used instead.
|
||||
type: path
|
||||
notes:
|
||||
- For information on using CLI and netconf see the :ref:`Junos OS Platform Options guide <junos_platform_options>`
|
||||
- For more information on using Ansible to manage network devices see the :ref:`Ansible Network Guide <network_guide>`
|
||||
- For more information on using Ansible to manage Juniper network devices see U(https://www.ansible.com/ansible-juniper).
|
||||
'''
|
@ -1,226 +0,0 @@
|
||||
#
|
||||
# (c) 2017 Red Hat Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# 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/>.
|
||||
#
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
author: Ansible Networking Team
|
||||
netconf: junos
|
||||
short_description: Use junos netconf plugin to run netconf commands on Juniper JUNOS platform
|
||||
description:
|
||||
- This junos plugin provides low level abstraction apis for
|
||||
sending and receiving netconf commands from Juniper JUNOS network devices.
|
||||
version_added: "2.9"
|
||||
options:
|
||||
ncclient_device_handler:
|
||||
type: str
|
||||
default: junos
|
||||
description:
|
||||
- Specifies the ncclient device handler name for Juniper junos network os. To
|
||||
identify the ncclient device handler name refer ncclient library documentation.
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
|
||||
from ansible.module_utils._text import to_text, to_native
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.plugins.netconf import NetconfBase, ensure_ncclient
|
||||
|
||||
try:
|
||||
from ncclient import manager
|
||||
from ncclient.operations import RPCError
|
||||
from ncclient.transport.errors import SSHUnknownHostError
|
||||
from ncclient.xml_ import to_ele, to_xml, new_ele, sub_ele
|
||||
HAS_NCCLIENT = True
|
||||
except (ImportError, AttributeError): # paramiko and gssapi are incompatible and raise AttributeError not ImportError
|
||||
HAS_NCCLIENT = False
|
||||
|
||||
|
||||
class Netconf(NetconfBase):
|
||||
def get_text(self, ele, tag):
|
||||
try:
|
||||
return to_text(ele.find(tag).text, errors='surrogate_then_replace').strip()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
@ensure_ncclient
|
||||
def get_device_info(self):
|
||||
device_info = dict()
|
||||
device_info['network_os'] = 'junos'
|
||||
ele = new_ele('get-software-information')
|
||||
data = self.execute_rpc(to_xml(ele))
|
||||
reply = to_ele(data)
|
||||
sw_info = reply.find('.//software-information')
|
||||
|
||||
device_info['network_os_version'] = self.get_text(sw_info, 'junos-version')
|
||||
device_info['network_os_hostname'] = self.get_text(sw_info, 'host-name')
|
||||
device_info['network_os_model'] = self.get_text(sw_info, 'product-model')
|
||||
|
||||
return device_info
|
||||
|
||||
def execute_rpc(self, name):
|
||||
"""
|
||||
RPC to be execute on remote device
|
||||
:param name: Name of rpc in string format
|
||||
:return: Received rpc response from remote host
|
||||
"""
|
||||
return self.rpc(name)
|
||||
|
||||
@ensure_ncclient
|
||||
def load_configuration(self, format='xml', action='merge', target='candidate', config=None):
|
||||
"""
|
||||
Load given configuration on device
|
||||
:param format: Format of configuration (xml, text, set)
|
||||
:param action: Action to be performed (merge, replace, override, update)
|
||||
:param target: The name of the configuration datastore being edited
|
||||
:param config: The configuration to be loaded on remote host in string format
|
||||
:return: Received rpc response from remote host in string format
|
||||
"""
|
||||
if config:
|
||||
if format == 'xml':
|
||||
config = to_ele(config)
|
||||
|
||||
try:
|
||||
return self.m.load_configuration(format=format, action=action, target=target, config=config).data_xml
|
||||
except RPCError as exc:
|
||||
raise Exception(to_xml(exc.xml))
|
||||
|
||||
def get_capabilities(self):
|
||||
result = dict()
|
||||
result['rpc'] = self.get_base_rpc() + ['commit', 'discard_changes', 'validate', 'lock', 'unlock', 'copy_copy',
|
||||
'execute_rpc', 'load_configuration', 'get_configuration', 'command',
|
||||
'reboot', 'halt']
|
||||
result['network_api'] = 'netconf'
|
||||
result['device_info'] = self.get_device_info()
|
||||
result['server_capabilities'] = [c for c in self.m.server_capabilities]
|
||||
result['client_capabilities'] = [c for c in self.m.client_capabilities]
|
||||
result['session_id'] = self.m.session_id
|
||||
result['device_operations'] = self.get_device_operations(result['server_capabilities'])
|
||||
return json.dumps(result)
|
||||
|
||||
@staticmethod
|
||||
@ensure_ncclient
|
||||
def guess_network_os(obj):
|
||||
"""
|
||||
Guess the remote network os name
|
||||
:param obj: Netconf connection class object
|
||||
:return: Network OS name
|
||||
"""
|
||||
try:
|
||||
m = manager.connect(
|
||||
host=obj._play_context.remote_addr,
|
||||
port=obj._play_context.port or 830,
|
||||
username=obj._play_context.remote_user,
|
||||
password=obj._play_context.password,
|
||||
key_filename=obj.key_filename,
|
||||
hostkey_verify=obj.get_option('host_key_checking'),
|
||||
look_for_keys=obj.get_option('look_for_keys'),
|
||||
allow_agent=obj._play_context.allow_agent,
|
||||
timeout=obj.get_option('persistent_connect_timeout'),
|
||||
# We need to pass in the path to the ssh_config file when guessing
|
||||
# the network_os so that a jumphost is correctly used if defined
|
||||
ssh_config=obj._ssh_config
|
||||
)
|
||||
except SSHUnknownHostError as exc:
|
||||
raise AnsibleConnectionFailure(to_native(exc))
|
||||
|
||||
guessed_os = None
|
||||
for c in m.server_capabilities:
|
||||
if re.search('junos', c):
|
||||
guessed_os = 'junos'
|
||||
|
||||
m.close_session()
|
||||
return guessed_os
|
||||
|
||||
def get_configuration(self, format='xml', filter=None):
|
||||
"""
|
||||
Retrieve all or part of a specified configuration.
|
||||
:param format: format in which configuration should be retrieved
|
||||
:param filter: specifies the portion of the configuration to retrieve
|
||||
as either xml string rooted in <configuration> element
|
||||
:return: Received rpc response from remote host in string format
|
||||
"""
|
||||
if filter is not None:
|
||||
if not isinstance(filter, string_types):
|
||||
raise AnsibleConnectionFailure("get configuration filter should be of type string,"
|
||||
" received value '%s' is of type '%s'" % (filter, type(filter)))
|
||||
filter = to_ele(filter)
|
||||
|
||||
return self.m.get_configuration(format=format, filter=filter).data_xml
|
||||
|
||||
def compare_configuration(self, rollback=0):
|
||||
"""
|
||||
Compare the candidate configuration with running configuration
|
||||
by default. The candidate configuration can be compared with older
|
||||
committed configuration by providing rollback id.
|
||||
:param rollback: Rollback id of previously commited configuration
|
||||
:return: Received rpc response from remote host in string format
|
||||
"""
|
||||
return self.m.compare_configuration(rollback=rollback).data_xml
|
||||
|
||||
def halt(self):
|
||||
"""reboot the device"""
|
||||
return self.m.halt().data_xml
|
||||
|
||||
def reboot(self):
|
||||
"""reboot the device"""
|
||||
return self.m.reboot().data_xml
|
||||
|
||||
# Due to issue in ncclient commit() method for Juniper (https://github.com/ncclient/ncclient/issues/238)
|
||||
# below commit() is a workaround which build's raw `commit-configuration` xml with required tags and uses
|
||||
# ncclient generic rpc() method to execute rpc on remote host.
|
||||
# Remove below method after the issue in ncclient is fixed.
|
||||
@ensure_ncclient
|
||||
def commit(self, confirmed=False, check=False, timeout=None, comment=None, synchronize=False, at_time=None):
|
||||
"""
|
||||
Commit the candidate configuration as the device's new current configuration.
|
||||
Depends on the `:candidate` capability.
|
||||
A confirmed commit (i.e. if *confirmed* is `True`) is reverted if there is no
|
||||
followup commit within the *timeout* interval. If no timeout is specified the
|
||||
confirm timeout defaults to 600 seconds (10 minutes).
|
||||
A confirming commit may have the *confirmed* parameter but this is not required.
|
||||
Depends on the `:confirmed-commit` capability.
|
||||
:param confirmed: whether this is a confirmed commit
|
||||
:param check: Check correctness of syntax
|
||||
:param timeout: specifies the confirm timeout in seconds
|
||||
:param comment: Message to write to commit log
|
||||
:param synchronize: Synchronize commit on remote peers
|
||||
:param at_time: Time at which to activate configuration changes
|
||||
:return: Received rpc response from remote host
|
||||
"""
|
||||
obj = new_ele('commit-configuration')
|
||||
if confirmed:
|
||||
sub_ele(obj, 'confirmed')
|
||||
if check:
|
||||
sub_ele(obj, 'check')
|
||||
if synchronize:
|
||||
sub_ele(obj, 'synchronize')
|
||||
if at_time:
|
||||
subele = sub_ele(obj, 'at-time')
|
||||
subele.text = str(at_time)
|
||||
if comment:
|
||||
subele = sub_ele(obj, 'log')
|
||||
subele.text = str(comment)
|
||||
if timeout:
|
||||
subele = sub_ele(obj, 'confirm-timeout')
|
||||
subele.text = str(timeout)
|
||||
return self.rpc(obj)
|
@ -1,53 +0,0 @@
|
||||
#
|
||||
# (c) 2016 Red Hat Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import re
|
||||
|
||||
from ansible.plugins.terminal import TerminalBase
|
||||
from ansible.errors import AnsibleConnectionFailure
|
||||
from ansible.utils.display import Display
|
||||
|
||||
display = Display()
|
||||
|
||||
|
||||
class TerminalModule(TerminalBase):
|
||||
|
||||
terminal_stdout_re = [
|
||||
re.compile(br"({primary:node\d+})?[\r\n]?[\w@+\-\.:\/\[\]]+[>#%] ?$"),
|
||||
]
|
||||
|
||||
terminal_stderr_re = [
|
||||
re.compile(br"unknown command"),
|
||||
re.compile(br"syntax error"),
|
||||
re.compile(br"[\r\n]error:")
|
||||
]
|
||||
|
||||
def on_open_shell(self):
|
||||
try:
|
||||
prompt = self._get_prompt()
|
||||
if prompt.strip().endswith(b'%'):
|
||||
display.vvv('starting cli', self._connection._play_context.remote_addr)
|
||||
self._exec_cli_command(b'cli')
|
||||
for c in (b'set cli timestamp disable', b'set cli screen-length 0', b'set cli screen-width 1024'):
|
||||
self._exec_cli_command(c)
|
||||
except AnsibleConnectionFailure:
|
||||
raise AnsibleConnectionFailure('unable to set terminal parameters')
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
testcase: "*"
|
||||
test_items: []
|
@ -1,2 +0,0 @@
|
||||
dependencies:
|
||||
- prepare_junos_tests
|
@ -1,2 +0,0 @@
|
||||
---
|
||||
- { include: netconf.yaml, tags: ['netconf'] }
|
@ -1,21 +0,0 @@
|
||||
- name: collect netconf test cases
|
||||
find:
|
||||
paths: "{{ role_path }}/tests/netconf"
|
||||
patterns: "{{ testcase }}.yaml"
|
||||
connection: local
|
||||
register: test_cases
|
||||
|
||||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=netconf)
|
||||
include: "{{ test_case_to_run }} ansible_connection=netconf"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
@ -1,208 +0,0 @@
|
||||
---
|
||||
- debug: msg="START junos_banner netconf/basic.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: setup - remove login banner
|
||||
junos_banner:
|
||||
banner: login
|
||||
state: absent
|
||||
provider: "{{ netconf }}"
|
||||
|
||||
- name: Create login banner
|
||||
junos_banner:
|
||||
banner: login
|
||||
text: this is my login banner
|
||||
state: present
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- name: Get running configuration
|
||||
junos_rpc:
|
||||
rpc: get-configuration
|
||||
provider: "{{ netconf }}"
|
||||
register: config
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'<message>this is my login banner</message>' in config.xml"
|
||||
|
||||
- name: Create login banner (idempotent)
|
||||
junos_banner:
|
||||
banner: login
|
||||
text: this is my login banner
|
||||
state: present
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
|
||||
- name: Deactivate login banner
|
||||
junos_banner:
|
||||
banner: login
|
||||
text: this is my login banner
|
||||
state: present
|
||||
active: False
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- name: Get running configuration
|
||||
junos_rpc:
|
||||
rpc: get-configuration
|
||||
provider: "{{ netconf }}"
|
||||
register: config
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'<message inactive=\"inactive\">this is my login banner</message>' in config.xml"
|
||||
|
||||
- name: Activate login banner
|
||||
junos_banner:
|
||||
banner: login
|
||||
text: this is my login banner
|
||||
state: present
|
||||
active: True
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- name: Get running configuration
|
||||
junos_rpc:
|
||||
rpc: get-configuration
|
||||
provider: "{{ netconf }}"
|
||||
register: config
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'<message>this is my login banner</message>' in config.xml"
|
||||
|
||||
- name: check mode
|
||||
junos_banner:
|
||||
banner: login
|
||||
text: this is not the login banner you're looking for
|
||||
state: present
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
check_mode: yes
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "result.failed == false"
|
||||
|
||||
- name: delete login banner
|
||||
junos_banner:
|
||||
banner: login
|
||||
state: absent
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- name: Get running configuration
|
||||
junos_rpc:
|
||||
rpc: get-configuration
|
||||
provider: "{{ netconf }}"
|
||||
register: config
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'<message>this is my login banner</message>' not in config.xml"
|
||||
|
||||
- name: setup - remove motd banner
|
||||
junos_banner:
|
||||
banner: motd
|
||||
state: absent
|
||||
provider: "{{ netconf }}"
|
||||
|
||||
- name: Create motd banner
|
||||
junos_banner:
|
||||
banner: motd
|
||||
text: this is my motd banner
|
||||
state: present
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- name: Get running configuration
|
||||
junos_rpc:
|
||||
rpc: get-configuration
|
||||
provider: "{{ netconf }}"
|
||||
register: config
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'<announcement>this is my motd banner</announcement>' in config.xml"
|
||||
|
||||
- name: Create motd banner (idempotent)
|
||||
junos_banner:
|
||||
banner: motd
|
||||
text: this is my motd banner
|
||||
state: present
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
|
||||
- name: Deactivate motd banner
|
||||
junos_banner:
|
||||
banner: motd
|
||||
text: this is my motd banner
|
||||
state: present
|
||||
active: False
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- name: Get running configuration
|
||||
junos_rpc:
|
||||
rpc: get-configuration
|
||||
provider: "{{ netconf }}"
|
||||
register: config
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'<announcement inactive=\"inactive\">this is my motd banner</announcement>' in config.xml"
|
||||
|
||||
- name: Activate motd banner
|
||||
junos_banner:
|
||||
banner: motd
|
||||
text: this is my motd banner
|
||||
state: present
|
||||
active: True
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- name: Get running configuration
|
||||
junos_rpc:
|
||||
rpc: get-configuration
|
||||
provider: "{{ netconf }}"
|
||||
register: config
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'<announcement>this is my motd banner</announcement>' in config.xml"
|
||||
|
||||
- name: delete motd banner
|
||||
junos_banner:
|
||||
banner: motd
|
||||
state: absent
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- name: Get running configuration
|
||||
junos_rpc:
|
||||
rpc: get-configuration
|
||||
provider: "{{ netconf }}"
|
||||
register: config
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'<announcement>this is my motd banner</announcement>' not in config.xml"
|
||||
|
||||
- debug: msg="END junos_banner netconf/basic.yaml on connection={{ ansible_connection }}"
|
@ -1,35 +0,0 @@
|
||||
---
|
||||
- debug: msg="START junos net_banner netconf/net_banner.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: setup - remove login banner
|
||||
net_banner:
|
||||
banner: login
|
||||
state: absent
|
||||
provider: "{{ netconf }}"
|
||||
|
||||
- name: Create login banner
|
||||
net_banner:
|
||||
banner: login
|
||||
text: this is my login banner configured by net_banner
|
||||
state: present
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- name: Get running configuration
|
||||
junos_rpc:
|
||||
rpc: get-configuration
|
||||
provider: "{{ netconf }}"
|
||||
register: config
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'<message>this is my login banner configured by net_banner</message>' in config.xml"
|
||||
|
||||
- name: teardown - remove login banner
|
||||
net_banner:
|
||||
banner: login
|
||||
state: absent
|
||||
provider: "{{ netconf }}"
|
||||
|
||||
- debug: msg="END junos net_banner netconf/net_banner.yaml on connection={{ ansible_connection }}"
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
testcase: "*"
|
||||
test_items: []
|
@ -1,2 +0,0 @@
|
||||
dependencies:
|
||||
- prepare_junos_tests
|
@ -1,16 +0,0 @@
|
||||
---
|
||||
- name: collect all cli test cases
|
||||
find:
|
||||
paths: "{{ role_path }}/tests/cli"
|
||||
patterns: "{{ testcase }}.yaml"
|
||||
register: test_cases
|
||||
delegate_to: localhost
|
||||
|
||||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=network_cli)
|
||||
include: "{{ test_case_to_run }} ansible_connection=network_cli"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
@ -1,5 +0,0 @@
|
||||
---
|
||||
- { include: netconf_xml.yaml, tags: ['netconf', 'xml'] }
|
||||
- { include: netconf_text.yaml, tags: ['netconf', 'text'] }
|
||||
- { include: netconf_json.yaml, tags: ['netconf', 'json'] }
|
||||
- { include: cli.yaml, tags: ['cli'] }
|
@ -1,22 +0,0 @@
|
||||
---
|
||||
- name: collect netconf_json test cases with json encoding
|
||||
find:
|
||||
paths: "{{ role_path }}/tests/netconf_json"
|
||||
patterns: "{{ testcase }}.yaml"
|
||||
connection: local
|
||||
register: test_cases
|
||||
|
||||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=netconf)
|
||||
include: "{{ test_case_to_run }} ansible_connection=netconf"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
@ -1,22 +0,0 @@
|
||||
---
|
||||
- name: collect netconf_text test cases with text encoding
|
||||
find:
|
||||
paths: "{{ role_path }}/tests/netconf_text"
|
||||
patterns: "{{ testcase }}.yaml"
|
||||
connection: local
|
||||
register: test_cases
|
||||
|
||||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=netconf)
|
||||
include: "{{ test_case_to_run }} ansible_connection=netconf"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
@ -1,22 +0,0 @@
|
||||
---
|
||||
- name: collect netconf_xml test cases with xml encoding
|
||||
find:
|
||||
paths: "{{ role_path }}/tests/netconf_xml"
|
||||
patterns: "{{ testcase }}.yaml"
|
||||
connection: local
|
||||
register: test_cases
|
||||
|
||||
- name: set test_items
|
||||
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
|
||||
|
||||
- name: run test case (connection=netconf)
|
||||
include: "{{ test_case_to_run }} ansible_connection=netconf"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
||||
|
||||
- name: run test case (connection=local)
|
||||
include: "{{ test_case_to_run }} ansible_connection=local"
|
||||
with_items: "{{ test_items }}"
|
||||
loop_control:
|
||||
loop_var: test_case_to_run
|
@ -1,64 +0,0 @@
|
||||
---
|
||||
- debug:
|
||||
msg: "START cli/cli_command.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- block:
|
||||
- name: get output for single command
|
||||
cli_command:
|
||||
command: show version
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
|
||||
- name: test with prompt and answer
|
||||
cli_command:
|
||||
command: "{{ item }}"
|
||||
prompt:
|
||||
- "Exit with uncommitted changes"
|
||||
answer: yes
|
||||
loop:
|
||||
- configure
|
||||
- set system syslog file test any any
|
||||
- rollback
|
||||
- exit
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
|
||||
- cli_command:
|
||||
command: "{{item}}"
|
||||
prompt:
|
||||
- "New password"
|
||||
- "Retype new password"
|
||||
answer:
|
||||
- "Test1234"
|
||||
- "Test1234"
|
||||
check_all: True
|
||||
loop:
|
||||
- "configure"
|
||||
- "rollback"
|
||||
- "set system login user ansible_test class operator authentication plain-text-password"
|
||||
- "commit"
|
||||
register: result
|
||||
ignore_errors: True
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "'failed' not in result"
|
||||
|
||||
- junos_netconf:
|
||||
register: result
|
||||
ignore_errors: True
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.failed == false"
|
||||
|
||||
when: ansible_connection == 'network_cli'
|
||||
|
||||
- debug: msg="END cli/cli_command.yaml on connection={{ ansible_connection }}"
|
@ -1,21 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_json/bad_operator.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test bad operator with json encoding
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
wait_for:
|
||||
- "result[0]['software-information'][0]['host-name'][0]['data'] foo lo0"
|
||||
format: json
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.failed == true"
|
||||
- "result.msg is defined"
|
||||
|
||||
- debug: msg="END netconf_json/bad_operator.yaml on connection={{ ansible_connection }}"
|
@ -1,21 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_json/contains.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test contains operator with json encoding
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
format: json
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['name'][0]['data'] contains lo0"
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- debug: msg="END netconf_json/contains.yaml on connection={{ ansible_connection }}"
|
@ -1,38 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_json/equal.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test == operator with xml encoding
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['name'][0]['data'] == lo0"
|
||||
format: json
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- name: test eq operator with json encoding
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['name'][0]['data'] eq lo0"
|
||||
format: json
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- debug: msg="END netconf_json/equal.yaml on connection={{ ansible_connection }}"
|
@ -1,38 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_json/greaterthan.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test gt operator
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
format: json
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['local-index'][0]['data'] gt 5"
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- name: test > operator
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
format: json
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['local-index'][0]['data'] > 5"
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- debug: msg="END netconf_json/greaterthan.yaml on connection={{ ansible_connection }}"
|
@ -1,38 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_json/greaterthanorequal.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test ge operator
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
format: json
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['local-index'][0]['data'] ge 6"
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- name: test >= operator
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
format: json
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['local-index'][0]['data'] >= 6"
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- debug: msg="END netconf_json/greaterthanorequal.yaml on connection={{ ansible_connection }}"
|
@ -1,38 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_json/lessthan.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test lt operator
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
format: json
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['local-index'][0]['data'] lt 7"
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- name: test < operator
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
format: json
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['local-index'][0]['data'] lt 7"
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- debug: msg="END netconf_json/lessthan.yaml on connection={{ ansible_connection }}"
|
@ -1,38 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_json/lessthanorequal.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test le operator
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
format: json
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['local-index'][0][data] le 6"
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- name: test <= operator
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
format: json
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['local-index'][0][data] <= 6"
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- debug: msg="END netconf_json/lessthanorequal.yaml on connection={{ ansible_connection }}"
|
@ -1,38 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_json/notequal.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test neq operator
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
format: json
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['name'][0]['data'] neq em0"
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- name: test != operator
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
format: json
|
||||
wait_for:
|
||||
- "result[1]['interface-information'][0]['physical-interface'][0]['name'][0]['data'] neq em0"
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- debug: msg="END netconf_json/notequal.yaml on connection={{ ansible_connection }}"
|
@ -1,63 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_json/output.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: get output for single command
|
||||
junos_command:
|
||||
commands: ['show version']
|
||||
format: json
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- name: get output for multiple commands
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show route
|
||||
format: json
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- name: get output for single command with cli transport
|
||||
junos_command:
|
||||
commands: ['show version | display json']
|
||||
provider:
|
||||
transport: cli
|
||||
register: result
|
||||
connection: network_cli
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- name: get output for multiple commands with cli transport
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show route
|
||||
format: json
|
||||
provider:
|
||||
transport: cli
|
||||
register: result
|
||||
connection: network_cli
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- debug: msg="END netconf_json/output.yaml on connection={{ ansible_connection }}"
|
@ -1,21 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_text/bad_operator.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test bad operator with text encoding
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
wait_for:
|
||||
- "result[1].interface-information[0].physical-interface[0].name[0].data foo lo0"
|
||||
encoding: text
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.failed == true"
|
||||
- "result.msg is defined"
|
||||
|
||||
- debug: msg="END netconf_text/bad_operator.yaml on connection={{ ansible_connection }}"
|
@ -1,21 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_text/contains.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test contains operator with text encoding
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
display: text
|
||||
wait_for:
|
||||
- "result[1] contains lo0"
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- debug: msg="END netconf_text/contains.yaml on connection={{ ansible_connection }}"
|
@ -1,34 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_text/invalid.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: run invalid command
|
||||
junos_command:
|
||||
commands: show foo
|
||||
display: text
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
- debug: var=result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.failed == true"
|
||||
- "result.msg is defined"
|
||||
|
||||
- name: run commands that include invalid command
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show foo
|
||||
display: text
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.failed == true"
|
||||
- "result.msg is defined"
|
||||
|
||||
- debug: msg="END netconf_text/invalid.yaml on connection={{ ansible_connection }}"
|
@ -1,62 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_text/output.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: get output for single command
|
||||
junos_command:
|
||||
commands: show version
|
||||
display: text
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- name: get output for multiple commands
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show route
|
||||
display: text
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- name: get output for single command with cli transport
|
||||
junos_command:
|
||||
commands: show version
|
||||
display: text
|
||||
provider:
|
||||
transport: cli
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- name: get output for multiple commands with cli transport
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show route
|
||||
display: text
|
||||
provider:
|
||||
transport: cli
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
- "result.stdout is defined"
|
||||
- "result.stdout_lines is defined"
|
||||
|
||||
- debug: msg="END netconf_text/output.yaml on connection={{ ansible_connection }}"
|
@ -1,20 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_text/timeout.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test bad condition
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
wait_for:
|
||||
- "result[0] contains bad_value_string"
|
||||
display: text
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.failed == true"
|
||||
- "result.msg is defined"
|
||||
|
||||
- debug: msg="END netconf_text/timeout.yaml on connection={{ ansible_connection }}"
|
@ -1,21 +0,0 @@
|
||||
---
|
||||
- debug: msg="START netconf_xml/bad_operator.yaml on connection={{ ansible_connection }}"
|
||||
|
||||
- name: test bad operator with xml encoding
|
||||
junos_command:
|
||||
commands:
|
||||
- show version
|
||||
- show interfaces lo0
|
||||
wait_for:
|
||||
- "result[1].rpc-reply.interface-information[0].physical-interface[0].name[0].data foo lo0"
|
||||
format: xml
|
||||
provider: "{{ netconf }}"
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- "result.failed == true"
|
||||
- "result.msg is defined"
|
||||
|
||||
- debug: msg="END netconf_xml/bad_operator.yaml on connection={{ ansible_connection }}"
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue