mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Junos fixes (#22423)
* Fixes for junos_config errors * Check transport settings for core Junos * Don't pop from the same list you iterate over * use of persistent connections are now explicitly enabled in junos * modules must now explicitly enable persistent connections * adds rpc support to junos_command fixes #22166
This commit is contained in:
parent
17fc6832ca
commit
1825406e1e
7 changed files with 207 additions and 86 deletions
|
@ -18,7 +18,7 @@
|
||||||
#
|
#
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from ncclient.xml_ import new_ele, sub_ele, to_xml
|
from xml.etree.ElementTree import Element, SubElement, tostring
|
||||||
|
|
||||||
from ansible.module_utils.basic import env_fallback
|
from ansible.module_utils.basic import env_fallback
|
||||||
from ansible.module_utils.netconf import send_request
|
from ansible.module_utils.netconf import send_request
|
||||||
|
@ -41,6 +41,7 @@ junos_argument_spec = {
|
||||||
'ssh_keyfile': dict(fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE']), type='path'),
|
'ssh_keyfile': dict(fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE']), type='path'),
|
||||||
'timeout': dict(type='int', default=10),
|
'timeout': dict(type='int', default=10),
|
||||||
'provider': dict(type='dict'),
|
'provider': dict(type='dict'),
|
||||||
|
'transport': dict(choices=['cli', 'netconf'])
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_args(module, warnings):
|
def check_args(module, warnings):
|
||||||
|
@ -81,17 +82,17 @@ def load_configuration(module, candidate=None, action='merge', rollback=None, fo
|
||||||
else:
|
else:
|
||||||
xattrs = {'action': action, 'format': format}
|
xattrs = {'action': action, 'format': format}
|
||||||
|
|
||||||
obj = new_ele('load-configuration', xattrs)
|
obj = Element('load-configuration', xattrs)
|
||||||
|
|
||||||
if candidate is not None:
|
if candidate is not None:
|
||||||
lookup = {'xml': 'configuration', 'text': 'configuration-text',
|
lookup = {'xml': 'configuration', 'text': 'configuration-text',
|
||||||
'set': 'configuration-set', 'json': 'configuration-json'}
|
'set': 'configuration-set', 'json': 'configuration-json'}
|
||||||
|
|
||||||
if action == 'set':
|
if action == 'set':
|
||||||
cfg = sub_ele(obj, 'configuration-set')
|
cfg = SubElement(obj, 'configuration-set')
|
||||||
cfg.text = '\n'.join(candidate)
|
cfg.text = '\n'.join(candidate)
|
||||||
else:
|
else:
|
||||||
cfg = sub_ele(obj, lookup[format])
|
cfg = SubElement(obj, lookup[format])
|
||||||
cfg.append(candidate)
|
cfg.append(candidate)
|
||||||
|
|
||||||
return send_request(module, obj)
|
return send_request(module, obj)
|
||||||
|
@ -104,22 +105,22 @@ def get_configuration(module, compare=False, format='xml', rollback='0'):
|
||||||
validate_rollback_id(rollback)
|
validate_rollback_id(rollback)
|
||||||
xattrs['compare'] = 'rollback'
|
xattrs['compare'] = 'rollback'
|
||||||
xattrs['rollback'] = str(rollback)
|
xattrs['rollback'] = str(rollback)
|
||||||
return send_request(module, new_ele('get-configuration', xattrs))
|
return send_request(module, Element('get-configuration', xattrs))
|
||||||
|
|
||||||
def commit_configuration(module, confirm=False, check=False, comment=None, confirm_timeout=None):
|
def commit_configuration(module, confirm=False, check=False, comment=None, confirm_timeout=None):
|
||||||
obj = new_ele('commit-configuration')
|
obj = Element('commit-configuration')
|
||||||
if confirm:
|
if confirm:
|
||||||
sub_ele(obj, 'confirmed')
|
SubElement(obj, 'confirmed')
|
||||||
if check:
|
if check:
|
||||||
sub_ele(obj, 'check')
|
SubElement(obj, 'check')
|
||||||
if comment:
|
if comment:
|
||||||
children(obj, ('log', str(comment)))
|
children(obj, ('log', str(comment)))
|
||||||
if confirm_timeout:
|
if confirm_timeout:
|
||||||
children(obj, ('confirm-timeout', int(confirm_timeout)))
|
children(obj, ('confirm-timeout', int(confirm_timeout)))
|
||||||
return send_request(module, obj)
|
return send_request(module, obj)
|
||||||
|
|
||||||
lock_configuration = lambda x: send_request(x, new_ele('lock-configuration'))
|
lock_configuration = lambda x: send_request(x, Element('lock-configuration'))
|
||||||
unlock_configuration = lambda x: send_request(x, new_ele('unlock-configuration'))
|
unlock_configuration = lambda x: send_request(x, Element('unlock-configuration'))
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def locked_config(module):
|
def locked_config(module):
|
||||||
|
@ -131,7 +132,7 @@ def locked_config(module):
|
||||||
|
|
||||||
def get_diff(module):
|
def get_diff(module):
|
||||||
reply = get_configuration(module, compare=True, format='text')
|
reply = get_configuration(module, compare=True, format='text')
|
||||||
output = reply.xpath('//configuration-output')
|
output = reply.find('.//configuration-output')
|
||||||
if output:
|
if output:
|
||||||
return output[0].text
|
return output[0].text
|
||||||
|
|
||||||
|
|
|
@ -26,50 +26,50 @@
|
||||||
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#
|
#
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
from xml.etree.ElementTree import Element, SubElement
|
||||||
from ncclient.xml_ import new_ele, sub_ele, to_xml, to_ele
|
from xml.etree.ElementTree import tostring, fromstring
|
||||||
|
|
||||||
from ansible.module_utils.connection import exec_command
|
from ansible.module_utils.connection import exec_command
|
||||||
|
|
||||||
def send_request(module, obj, check_rc=True):
|
def send_request(module, obj, check_rc=True):
|
||||||
request = to_xml(obj)
|
request = tostring(obj)
|
||||||
rc, out, err = exec_command(module, request)
|
rc, out, err = exec_command(module, request)
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
if check_rc:
|
if check_rc:
|
||||||
module.fail_json(msg=str(err))
|
module.fail_json(msg=str(err))
|
||||||
return to_ele(err)
|
return fromstring(out)
|
||||||
return to_ele(out)
|
return fromstring(out)
|
||||||
|
|
||||||
def children(root, iterable):
|
def children(root, iterable):
|
||||||
for item in iterable:
|
for item in iterable:
|
||||||
try:
|
try:
|
||||||
ele = sub_ele(ele, item)
|
ele = SubElement(ele, item)
|
||||||
except NameError:
|
except NameError:
|
||||||
ele = sub_ele(root, item)
|
ele = SubElement(root, item)
|
||||||
|
|
||||||
def lock(module, target='candidate'):
|
def lock(module, target='candidate'):
|
||||||
obj = new_ele('lock')
|
obj = Element('lock')
|
||||||
children(obj, ('target', target))
|
children(obj, ('target', target))
|
||||||
return send_request(module, obj)
|
return send_request(module, obj)
|
||||||
|
|
||||||
def unlock(module, target='candidate'):
|
def unlock(module, target='candidate'):
|
||||||
obj = new_ele('unlock')
|
obj = Element('unlock')
|
||||||
children(obj, ('target', target))
|
children(obj, ('target', target))
|
||||||
return send_request(module, obj)
|
return send_request(module, obj)
|
||||||
|
|
||||||
def commit(module):
|
def commit(module):
|
||||||
return send_request(module, new_ele('commit'))
|
return send_request(module, Element('commit'))
|
||||||
|
|
||||||
def discard_changes(module):
|
def discard_changes(module):
|
||||||
return send_request(module, new_ele('discard-changes'))
|
return send_request(module, Element('discard-changes'))
|
||||||
|
|
||||||
def validate(module):
|
def validate(module):
|
||||||
obj = new_ele('validate')
|
obj = Element('validate')
|
||||||
children(obj, ('source', 'candidate'))
|
children(obj, ('source', 'candidate'))
|
||||||
return send_request(module, obj)
|
return send_request(module, obj)
|
||||||
|
|
||||||
def get_config(module, source='running', filter=None):
|
def get_config(module, source='running', filter=None):
|
||||||
obj = new_ele('get-config')
|
obj = Element('get-config')
|
||||||
children(obj, ('source', source))
|
children(obj, ('source', source))
|
||||||
children(obj, ('filter', filter))
|
children(obj, ('filter', filter))
|
||||||
return send_request(module, obj)
|
return send_request(module, obj)
|
||||||
|
|
|
@ -116,8 +116,16 @@ from ansible.module_utils.junos import check_args, junos_argument_spec
|
||||||
from ansible.module_utils.junos import get_configuration, load
|
from ansible.module_utils.junos import get_configuration, load
|
||||||
from ansible.module_utils.six import text_type
|
from ansible.module_utils.six import text_type
|
||||||
|
|
||||||
|
USE_PERSISTENT_CONNECTION = True
|
||||||
DEFAULT_COMMENT = 'configured by junos_template'
|
DEFAULT_COMMENT = 'configured by junos_template'
|
||||||
|
|
||||||
|
def check_transport(module):
|
||||||
|
transport = (module.params['provider'] or {}).get('transport')
|
||||||
|
|
||||||
|
if transport == 'netconf':
|
||||||
|
module.fail_json(msg='junos_template module is only supported over cli transport')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
|
@ -127,7 +135,6 @@ def main():
|
||||||
action=dict(default='merge', choices=['merge', 'overwrite', 'replace']),
|
action=dict(default='merge', choices=['merge', 'overwrite', 'replace']),
|
||||||
config_format=dict(choices=['text', 'set', 'xml']),
|
config_format=dict(choices=['text', 'set', 'xml']),
|
||||||
backup=dict(default=False, type='bool'),
|
backup=dict(default=False, type='bool'),
|
||||||
transport=dict(default='netconf', choices=['netconf'])
|
|
||||||
)
|
)
|
||||||
|
|
||||||
argument_spec.update(junos_argument_spec)
|
argument_spec.update(junos_argument_spec)
|
||||||
|
@ -135,6 +142,8 @@ def main():
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
check_transport(module)
|
||||||
|
|
||||||
warnings = list()
|
warnings = list()
|
||||||
check_args(module, warnings)
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
|
|
@ -127,17 +127,21 @@ failed_conditions:
|
||||||
sample: ['...', '...']
|
sample: ['...', '...']
|
||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
|
import re
|
||||||
|
import shlex
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from xml.etree import ElementTree as etree
|
from xml.etree import ElementTree as etree
|
||||||
|
from xml.etree.ElementTree import Element, SubElement, tostring
|
||||||
|
|
||||||
|
|
||||||
from ansible.module_utils.junos import run_commands
|
from ansible.module_utils.junos import run_commands
|
||||||
from ansible.module_utils.junos import junos_argument_spec
|
from ansible.module_utils.junos import junos_argument_spec, check_args
|
||||||
from ansible.module_utils.junos import check_args as junos_check_args
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.six import string_types
|
|
||||||
from ansible.module_utils.netcli import Conditional, FailedConditionalError
|
from ansible.module_utils.netcli import Conditional, FailedConditionalError
|
||||||
from ansible.module_utils.network_common import ComplexList
|
from ansible.module_utils.netconf import send_request
|
||||||
|
from ansible.module_utils.network_common import ComplexList, to_list
|
||||||
|
from ansible.module_utils.six import string_types, iteritems
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import jxmlease
|
import jxmlease
|
||||||
|
@ -145,12 +149,22 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
HAS_JXMLEASE = False
|
HAS_JXMLEASE = False
|
||||||
|
|
||||||
def check_args(module, warnings):
|
USE_PERSISTENT_CONNECTION = True
|
||||||
junos_check_args(module, warnings)
|
|
||||||
|
|
||||||
if module.params['rpcs']:
|
|
||||||
module.fail_json(msg='argument rpcs has been deprecated, please use '
|
VALID_KEYS = {
|
||||||
'junos_rpc instead')
|
'cli': frozenset(['command', 'output', 'prompt', 'response']),
|
||||||
|
'rpc': frozenset(['command', 'output'])
|
||||||
|
}
|
||||||
|
|
||||||
|
def check_transport(module):
|
||||||
|
transport = (module.params['provider'] or {}).get('transport')
|
||||||
|
|
||||||
|
if transport == 'netconf' and not module.params['rpcs']:
|
||||||
|
module.fail_json(msg='argument commands is only supported over cli transport')
|
||||||
|
|
||||||
|
elif transport == 'cli' and not module.params['commands']:
|
||||||
|
module.fail_json(msg='argument rpcs is only supported over netconf transport')
|
||||||
|
|
||||||
def to_lines(stdout):
|
def to_lines(stdout):
|
||||||
lines = list()
|
lines = list()
|
||||||
|
@ -160,7 +174,78 @@ def to_lines(stdout):
|
||||||
lines.append(item)
|
lines.append(item)
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def parse_commands(module, warnings):
|
def run_rpcs(module, items):
|
||||||
|
|
||||||
|
responses = list()
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
name = item['name']
|
||||||
|
args = item['args']
|
||||||
|
|
||||||
|
name = str(name).replace('_', '-')
|
||||||
|
|
||||||
|
if all((module.check_mode, not name.startswith('get'))):
|
||||||
|
module.fail_json(msg='invalid rpc for running in check_mode')
|
||||||
|
|
||||||
|
xattrs = {'format': item['output']}
|
||||||
|
|
||||||
|
element = Element(name, 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 = send_request(module, element)
|
||||||
|
|
||||||
|
if module.params['display'] == 'text':
|
||||||
|
data = reply.find('.//output')
|
||||||
|
responses.append(data.text.strip())
|
||||||
|
elif module.params['display'] == 'json':
|
||||||
|
responses.append(module.from_json(reply.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']:
|
||||||
|
parts = 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)
|
||||||
|
|
||||||
|
output = module.params['display'] or 'xml'
|
||||||
|
items.append({'name': name, 'args': args, 'output': output})
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
def parse_commands(module):
|
||||||
spec = dict(
|
spec = dict(
|
||||||
command=dict(key=True),
|
command=dict(key=True),
|
||||||
output=dict(default=module.params['display'], choices=['text', 'json', 'xml']),
|
output=dict(default=module.params['display'], choices=['text', 'json', 'xml']),
|
||||||
|
@ -178,6 +263,8 @@ def parse_commands(module, warnings):
|
||||||
'executing %s' % item['command']
|
'executing %s' % item['command']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if item['command'].startswith('show configuration'):
|
||||||
|
item['output'] = 'text'
|
||||||
if item['output'] == 'json' and 'display json' not in item['command']:
|
if item['output'] == 'json' and 'display json' not in item['command']:
|
||||||
item['command'] += '| display json'
|
item['command'] += '| display json'
|
||||||
elif item['output'] == 'xml' and 'display xml' not in item['command']:
|
elif item['output'] == 'xml' and 'display xml' not in item['command']:
|
||||||
|
@ -195,12 +282,11 @@ def main():
|
||||||
"""entry point for module execution
|
"""entry point for module execution
|
||||||
"""
|
"""
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
commands=dict(type='list', required=True),
|
commands=dict(type='list'),
|
||||||
display=dict(choices=['text', 'json', 'xml'], default='text', aliases=['format', 'output']),
|
|
||||||
|
|
||||||
# deprecated (Ansible 2.3) - use junos_rpc
|
|
||||||
rpcs=dict(type='list'),
|
rpcs=dict(type='list'),
|
||||||
|
|
||||||
|
display=dict(choices=['text', 'json', 'xml'], aliases=['format', 'output']),
|
||||||
|
|
||||||
wait_for=dict(type='list', aliases=['waitfor']),
|
wait_for=dict(type='list', aliases=['waitfor']),
|
||||||
match=dict(default='all', choices=['all', 'any']),
|
match=dict(default='all', choices=['all', 'any']),
|
||||||
|
|
||||||
|
@ -210,14 +296,25 @@ def main():
|
||||||
|
|
||||||
argument_spec.update(junos_argument_spec)
|
argument_spec.update(junos_argument_spec)
|
||||||
|
|
||||||
|
mutually_exclusive = [('commands', 'rpcs')]
|
||||||
|
|
||||||
|
required_one_of = [('commands', 'rpcs')]
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
|
mutually_exclusive=mutually_exclusive,
|
||||||
|
required_one_of=required_one_of,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
check_transport(module)
|
||||||
|
|
||||||
warnings = list()
|
warnings = list()
|
||||||
check_args(module, warnings)
|
check_args(module, warnings)
|
||||||
|
|
||||||
commands = parse_commands(module, warnings)
|
if module.params['commands']:
|
||||||
|
items = parse_commands(module)
|
||||||
|
else:
|
||||||
|
items = parse_rpcs(module)
|
||||||
|
|
||||||
|
|
||||||
wait_for = module.params['wait_for'] or list()
|
wait_for = module.params['wait_for'] or list()
|
||||||
display = module.params['display']
|
display = module.params['display']
|
||||||
|
@ -228,21 +325,29 @@ def main():
|
||||||
match = module.params['match']
|
match = module.params['match']
|
||||||
|
|
||||||
while retries > 0:
|
while retries > 0:
|
||||||
responses = run_commands(module, commands)
|
if module.params['commands']:
|
||||||
|
responses = run_commands(module, items)
|
||||||
|
else:
|
||||||
|
responses = run_rpcs(module, items)
|
||||||
|
|
||||||
for index, (resp, cmd) in enumerate(zip(responses, commands)):
|
transformed = list()
|
||||||
if cmd['output'] == 'xml':
|
|
||||||
|
for item, resp in zip(items, responses):
|
||||||
|
if item['output'] == 'xml':
|
||||||
if not HAS_JXMLEASE:
|
if not HAS_JXMLEASE:
|
||||||
module.fail_json(msg='jxmlease is required but does not appear to '
|
module.fail_json(msg='jxmlease is required but does not appear to '
|
||||||
'be installed. It can be installed using `pip install jxmlease`')
|
'be installed. It can be installed using `pip install jxmlease`')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
responses[index] = jxmlease.parse(resp)
|
transformed.append(jxmlease.parse(resp))
|
||||||
except:
|
except:
|
||||||
raise ValueError(resp)
|
raise ValueError(resp)
|
||||||
|
else:
|
||||||
|
transformed.append(resp)
|
||||||
|
|
||||||
for item in list(conditionals):
|
for item in list(conditionals):
|
||||||
try:
|
try:
|
||||||
if item(responses):
|
if item(transformed):
|
||||||
if match == 'any':
|
if match == 'any':
|
||||||
conditionals = list()
|
conditionals = list()
|
||||||
break
|
break
|
||||||
|
|
|
@ -178,18 +178,24 @@ import re
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
from ncclient.xml_ import to_xml
|
|
||||||
|
|
||||||
from ansible.module_utils.junos import get_diff, load
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from ansible.module_utils.junos import get_config, get_diff, load_config
|
||||||
from ansible.module_utils.junos import junos_argument_spec
|
from ansible.module_utils.junos import junos_argument_spec
|
||||||
from ansible.module_utils.junos import check_args as junos_check_args
|
from ansible.module_utils.junos import check_args as junos_check_args
|
||||||
from ansible.module_utils.junos import locked_config, load_configuration
|
|
||||||
from ansible.module_utils.junos import get_configuration
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.netcfg import NetworkConfig
|
from ansible.module_utils.netcfg import NetworkConfig
|
||||||
|
from ansible.module_utils.six import string_types
|
||||||
|
|
||||||
|
USE_PERSISTENT_CONNECTION = True
|
||||||
DEFAULT_COMMENT = 'configured by junos_config'
|
DEFAULT_COMMENT = 'configured by junos_config'
|
||||||
|
|
||||||
|
def check_transport(module):
|
||||||
|
transport = (module.params['provider'] or {}).get('transport')
|
||||||
|
|
||||||
|
if transport == 'netconf':
|
||||||
|
module.fail_json(msg='junos_config module is only supported over cli transport')
|
||||||
|
|
||||||
|
|
||||||
def check_args(module, warnings):
|
def check_args(module, warnings):
|
||||||
junos_check_args(module, warnings)
|
junos_check_args(module, warnings)
|
||||||
if module.params['zeroize']:
|
if module.params['zeroize']:
|
||||||
|
@ -219,7 +225,7 @@ def guess_format(config):
|
||||||
|
|
||||||
def config_to_commands(config):
|
def config_to_commands(config):
|
||||||
set_format = config.startswith('set') or config.startswith('delete')
|
set_format = config.startswith('set') or config.startswith('delete')
|
||||||
candidate = NetworkConfig(indent=4, contents=config, device_os='junos')
|
candidate = NetworkConfig(indent=4, contents=config)
|
||||||
if not set_format:
|
if not set_format:
|
||||||
candidate = [c.line for c in candidate.items]
|
candidate = [c.line for c in candidate.items]
|
||||||
commands = list()
|
commands = list()
|
||||||
|
@ -237,23 +243,21 @@ def config_to_commands(config):
|
||||||
return commands
|
return commands
|
||||||
|
|
||||||
def filter_delete_statements(module, candidate):
|
def filter_delete_statements(module, candidate):
|
||||||
reply = get_configuration(module, format='set')
|
config = get_config(module)
|
||||||
config = reply.xpath('//configuration-set')[0].text.strip()
|
|
||||||
|
modified_candidate = candidate[:]
|
||||||
for index, line in enumerate(candidate):
|
for index, line in enumerate(candidate):
|
||||||
if line.startswith('delete'):
|
if line.startswith('delete'):
|
||||||
newline = re.sub('^delete', 'set', line)
|
newline = re.sub('^delete', 'set', line)
|
||||||
if newline not in config:
|
if newline not in config:
|
||||||
del candidate[index]
|
del modified_candidate[index]
|
||||||
return candidate
|
return modified_candidate
|
||||||
|
|
||||||
def load_config(module):
|
def load(module):
|
||||||
candidate = module.params['lines'] or module.params['src']
|
candidate = module.params['lines'] or module.params['src']
|
||||||
if isinstance(candidate, basestring):
|
if isinstance(candidate, string_types):
|
||||||
candidate = candidate.split('\n')
|
candidate = candidate.split('\n')
|
||||||
|
|
||||||
confirm = module.params['confirm'] > 0
|
|
||||||
confirm_timeout = module.params['confirm']
|
|
||||||
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'confirm': module.params['confirm'] is not None,
|
'confirm': module.params['confirm'] is not None,
|
||||||
'confirm_timeout': module.params['confirm'],
|
'confirm_timeout': module.params['confirm'],
|
||||||
|
@ -269,23 +273,8 @@ def load_config(module):
|
||||||
# nothing in the config as that will cause an exception to be raised
|
# nothing in the config as that will cause an exception to be raised
|
||||||
if module.params['lines']:
|
if module.params['lines']:
|
||||||
candidate = filter_delete_statements(module, candidate)
|
candidate = filter_delete_statements(module, candidate)
|
||||||
kwargs.update({'action': 'set', 'format': 'text'})
|
|
||||||
|
|
||||||
return load(module, candidate, **kwargs)
|
return load_config(module, candidate, **kwargs)
|
||||||
|
|
||||||
def rollback_config(module, result):
|
|
||||||
rollback = module.params['rollback']
|
|
||||||
diff = None
|
|
||||||
|
|
||||||
with locked_config:
|
|
||||||
load_configuration(module, rollback=rollback)
|
|
||||||
diff = get_diff(module)
|
|
||||||
|
|
||||||
return diff
|
|
||||||
|
|
||||||
def confirm_config(module):
|
|
||||||
with locked_config:
|
|
||||||
commit_configuration(confirm=True)
|
|
||||||
|
|
||||||
def update_result(module, result, diff=None):
|
def update_result(module, result, diff=None):
|
||||||
if diff == '':
|
if diff == '':
|
||||||
|
@ -329,23 +318,22 @@ def main():
|
||||||
mutually_exclusive=mutually_exclusive,
|
mutually_exclusive=mutually_exclusive,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
check_transport(module)
|
||||||
|
|
||||||
warnings = list()
|
warnings = list()
|
||||||
check_args(module, warnings)
|
check_args(module, warnings)
|
||||||
|
|
||||||
result = {'changed': False, 'warnings': warnings}
|
result = {'changed': False, 'warnings': warnings}
|
||||||
|
|
||||||
if module.params['backup']:
|
if module.params['backup']:
|
||||||
result['__backup__'] = get_configuration()
|
result['__backup__'] = get_config(module)
|
||||||
|
|
||||||
if module.params['rollback']:
|
if module.params['rollback']:
|
||||||
diff = get_diff(module)
|
diff = get_diff(module)
|
||||||
update_result(module, result, diff)
|
update_result(module, result, diff)
|
||||||
|
|
||||||
elif not any((module.params['src'], module.params['lines'])):
|
|
||||||
confirm_config(module)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
diff = load_config(module)
|
diff = load(module)
|
||||||
update_result(module, result, diff)
|
update_result(module, result, diff)
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
|
@ -82,6 +82,14 @@ from ansible.module_utils.junos import junos_argument_spec, check_args
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.six import iteritems
|
from ansible.module_utils.six import iteritems
|
||||||
|
|
||||||
|
USE_PERSISTENT_CONNECTION = True
|
||||||
|
|
||||||
|
def check_transport(module):
|
||||||
|
transport = (module.params['provider'] or {}).get('transport')
|
||||||
|
|
||||||
|
if transport == 'netconf':
|
||||||
|
module.fail_json(msg='junos_netconf module is only supported over cli transport')
|
||||||
|
|
||||||
|
|
||||||
def map_obj_to_commands(updates, module):
|
def map_obj_to_commands(updates, module):
|
||||||
want, have = updates
|
want, have = updates
|
||||||
|
@ -145,10 +153,13 @@ def main():
|
||||||
)
|
)
|
||||||
|
|
||||||
argument_spec.update(junos_argument_spec)
|
argument_spec.update(junos_argument_spec)
|
||||||
|
argument_spec['transport'] = dict(choices=['cli'], default='cli')
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
|
check_transport(module)
|
||||||
|
|
||||||
warnings = list()
|
warnings = list()
|
||||||
check_args(module, warnings)
|
check_args(module, warnings)
|
||||||
|
|
||||||
|
@ -163,7 +174,7 @@ def main():
|
||||||
if commands:
|
if commands:
|
||||||
commit = not module.check_mode
|
commit = not module.check_mode
|
||||||
diff = load_config(module, commands, commit=commit)
|
diff = load_config(module, commands, commit=commit)
|
||||||
if diff and module._diff:
|
if diff:
|
||||||
if module._diff:
|
if module._diff:
|
||||||
result['diff'] = {'prepared': diff}
|
result['diff'] = {'prepared': diff}
|
||||||
result['changed'] = True
|
result['changed'] = True
|
||||||
|
|
|
@ -25,7 +25,7 @@ import copy
|
||||||
|
|
||||||
from ansible.plugins.action.normal import ActionModule as _ActionModule
|
from ansible.plugins.action.normal import ActionModule as _ActionModule
|
||||||
from ansible.utils.path import unfrackpath
|
from ansible.utils.path import unfrackpath
|
||||||
from ansible.plugins import connection_loader
|
from ansible.plugins import connection_loader, module_loader
|
||||||
from ansible.compat.six import iteritems
|
from ansible.compat.six import iteritems
|
||||||
from ansible.module_utils.junos import junos_argument_spec
|
from ansible.module_utils.junos import junos_argument_spec
|
||||||
from ansible.module_utils.basic import AnsibleFallbackNotFound
|
from ansible.module_utils.basic import AnsibleFallbackNotFound
|
||||||
|
@ -50,12 +50,18 @@ class ActionModule(_ActionModule):
|
||||||
'got %s' % self._play_context.connection
|
'got %s' % self._play_context.connection
|
||||||
)
|
)
|
||||||
|
|
||||||
|
module = module_loader._load_module_source(self._task.action, module_loader.find_plugin(self._task.action))
|
||||||
|
|
||||||
|
if not getattr(module, 'USE_PERSISTENT_CONNECTION', False):
|
||||||
|
return super(ActionModule, self).run(tmp, task_vars)
|
||||||
|
|
||||||
provider = self.load_provider()
|
provider = self.load_provider()
|
||||||
|
transport = provider['transport'] or 'cli'
|
||||||
|
|
||||||
pc = copy.deepcopy(self._play_context)
|
pc = copy.deepcopy(self._play_context)
|
||||||
pc.network_os = 'junos'
|
pc.network_os = 'junos'
|
||||||
|
|
||||||
if self._task.action in ('junos_command', 'junos_netconf', 'junos_config', '_junos_template'):
|
if transport == 'cli':
|
||||||
pc.connection = 'network_cli'
|
pc.connection = 'network_cli'
|
||||||
pc.port = provider['port'] or self._play_context.port or 22
|
pc.port = provider['port'] or self._play_context.port or 22
|
||||||
else:
|
else:
|
||||||
|
@ -82,6 +88,7 @@ class ActionModule(_ActionModule):
|
||||||
|
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return {'failed': True, 'msg': 'unable to connect to control socket'}
|
return {'failed': True, 'msg': 'unable to connect to control socket'}
|
||||||
|
|
||||||
elif pc.connection == 'network_cli':
|
elif pc.connection == 'network_cli':
|
||||||
# make sure we are in the right cli context which should be
|
# make sure we are in the right cli context which should be
|
||||||
# enable mode and not config module
|
# enable mode and not config module
|
||||||
|
|
Loading…
Reference in a new issue