updates junos_command to send commands over netconf (#22540)

* junos_command should send all cli commands over netconf
* task based credentials are now optional
* commands and rpcs are not mutually exclusive anymore
This commit is contained in:
Peter Sprygada 2017-03-12 08:45:49 -05:00 committed by GitHub
parent 3f0f7c4f4e
commit 995497129e

View file

@ -27,9 +27,9 @@ DOCUMENTATION = """
module: junos_command
version_added: "2.1"
author: "Peter Sprygada (@privateip)"
short_description: Run arbitrary commands on an Juniper junos device
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
- 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.
@ -42,7 +42,16 @@ options:
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.
required: true
required: false
default: null
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.
required: false
default: null
version_added: "2.3"
wait_for:
description:
- Specifies what to evaluate from the output of the command
@ -85,9 +94,6 @@ options:
"""
EXAMPLES = """
# Note: examples below use the following provider dict to handle
# transport and authentication to the node.
---
- name: run show version on remote devices
junos_command:
commands: show version
@ -114,9 +120,13 @@ EXAMPLES = """
- name: run commands and specify the output format
junos_command:
commands:
- command: show version
output: json
commands: show version
display: json
- name: run rpc on the remote device
junos_command:
rpcs: get-software-information
"""
RETURN = """
@ -135,7 +145,6 @@ 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 junos_argument_spec, check_args
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.netcli import Conditional, FailedConditionalError
@ -174,42 +183,49 @@ def to_lines(stdout):
lines.append(item)
return lines
def run_rpcs(module, items):
def rpc(module, items):
responses = list()
for item in items:
name = item['name']
args = item['args']
xattrs = item['xattrs']
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')
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:
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 item is not True:
child.text = item
else:
child = SubElement(element, key)
if value is not True:
child.text = value
if value is not True:
child.text = value
reply = send_request(module, element)
if module.params['display'] == 'text':
if xattrs['format'] == 'text':
data = reply.find('.//output')
responses.append(data.text.strip())
elif module.params['display'] == 'json':
elif xattrs['format'] == 'json':
responses.append(module.from_json(reply.text.strip()))
else:
responses.append(tostring(reply))
@ -224,7 +240,8 @@ def split(value):
def parse_rpcs(module):
items = list()
for rpc in module.params['rpcs']:
for rpc in (module.params['rpcs'] or list()):
parts = split(rpc)
name = parts.pop(0)
@ -239,44 +256,39 @@ def parse_rpcs(module):
else:
args[key] = str(value)
output = module.params['display'] or 'xml'
items.append({'name': name, 'args': args, 'output': output})
display = module.params['display'] or 'xml'
xattrs = {'format': display}
items.append({'name': name, 'args': args, 'xattrs': xattrs})
return items
def parse_commands(module):
spec = dict(
command=dict(key=True),
output=dict(default=module.params['display'], choices=['text', 'json', 'xml']),
prompt=dict(),
answer=dict()
)
items = list()
transform = ComplexList(spec, module)
commands = transform(module.params['commands'])
for index, item in enumerate(commands):
if module.check_mode and not item['command'].startswith('show'):
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' % item['command']
'executing %s' % command
)
if item['command'].startswith('show configuration'):
item['output'] = 'text'
if item['output'] == 'json' and 'display json' not in item['command']:
item['command'] += '| display json'
elif item['output'] == 'xml' and 'display xml' not in item['command']:
item['command'] += '| display xml'
else:
if '| display json' in item['command']:
item['command'] = str(item['command']).replace(' | display json', '')
elif '| display xml' in item['command']:
item['command'] = str(item['command']).replace(' | display xml', '')
commands[index] = item
parts = command.split('|')
text = parts[0]
display = module.params['display'] or 'text'
xattrs = {'format': display}
if '| display json' in command:
xattrs['format'] = 'json'
elif '| display xml' in command:
xattrs['format'] = 'xml'
items.append({'name': 'command', 'xattrs': xattrs, 'text': text})
return items
return commands
def main():
"""entry point for module execution
@ -296,12 +308,9 @@ def main():
argument_spec.update(junos_argument_spec)
mutually_exclusive = [('commands', 'rpcs')]
required_one_of = [('commands', 'rpcs')]
module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
required_one_of=required_one_of,
supports_check_mode=True)
@ -310,11 +319,9 @@ def main():
warnings = list()
check_args(module, warnings)
if module.params['commands']:
items = parse_commands(module)
else:
items = parse_rpcs(module)
items = list()
items.extend(parse_commands(module))
items.extend(parse_rpcs(module))
wait_for = module.params['wait_for'] or list()
display = module.params['display']
@ -325,15 +332,12 @@ def main():
match = module.params['match']
while retries > 0:
if module.params['commands']:
responses = run_commands(module, items)
else:
responses = run_rpcs(module, items)
responses = rpc(module, items)
transformed = list()
for item, resp in zip(items, responses):
if item['output'] == 'xml':
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`')