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 module: junos_command
version_added: "2.1" version_added: "2.1"
author: "Peter Sprygada (@privateip)" 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: 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 read from the device. This module includes an
argument that will cause the module to wait for a specific condition argument that will cause the module to wait for a specific condition
before returning or timing out if the condition is not met. 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 is returned. If the I(wait_for) argument is provided, the
module is not returned until the condition is satisfied or module is not returned until the condition is satisfied or
the number of I(retries) has been exceeded. 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: wait_for:
description: description:
- Specifies what to evaluate from the output of the command - Specifies what to evaluate from the output of the command
@ -85,9 +94,6 @@ options:
""" """
EXAMPLES = """ EXAMPLES = """
# Note: examples below use the following provider dict to handle
# transport and authentication to the node.
---
- name: run show version on remote devices - name: run show version on remote devices
junos_command: junos_command:
commands: show version commands: show version
@ -114,9 +120,13 @@ EXAMPLES = """
- name: run commands and specify the output format - name: run commands and specify the output format
junos_command: junos_command:
commands: commands: show version
- command: show version display: json
output: json
- name: run rpc on the remote device
junos_command:
rpcs: get-software-information
""" """
RETURN = """ RETURN = """
@ -135,7 +145,6 @@ from xml.etree import ElementTree as etree
from xml.etree.ElementTree import Element, SubElement, tostring 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.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.netcli import Conditional, FailedConditionalError from ansible.module_utils.netcli import Conditional, FailedConditionalError
@ -174,23 +183,28 @@ def to_lines(stdout):
lines.append(item) lines.append(item)
return lines return lines
def run_rpcs(module, items): def rpc(module, items):
responses = list() responses = list()
for item in items: for item in items:
name = item['name'] name = item['name']
args = item['args'] xattrs = item['xattrs']
args = item.get('args')
text = item.get('text')
name = str(name).replace('_', '-') name = str(name).replace('_', '-')
if all((module.check_mode, not name.startswith('get'))): if all((module.check_mode, not name.startswith('get'))):
module.fail_json(msg='invalid rpc for running in check_mode') module.fail_json(msg='invalid rpc for running in check_mode')
xattrs = {'format': item['output']}
element = Element(name, xattrs) element = Element(name, xattrs)
if text:
element.text = text
elif args:
for key, value in iteritems(args): for key, value in iteritems(args):
key = str(key).replace('_', '-') key = str(key).replace('_', '-')
if isinstance(value, list): if isinstance(value, list):
@ -205,11 +219,13 @@ def run_rpcs(module, items):
reply = send_request(module, element) reply = send_request(module, element)
if module.params['display'] == 'text': if xattrs['format'] == 'text':
data = reply.find('.//output') data = reply.find('.//output')
responses.append(data.text.strip()) responses.append(data.text.strip())
elif module.params['display'] == 'json':
elif xattrs['format'] == 'json':
responses.append(module.from_json(reply.text.strip())) responses.append(module.from_json(reply.text.strip()))
else: else:
responses.append(tostring(reply)) responses.append(tostring(reply))
@ -224,7 +240,8 @@ def split(value):
def parse_rpcs(module): def parse_rpcs(module):
items = list() items = list()
for rpc in module.params['rpcs']:
for rpc in (module.params['rpcs'] or list()):
parts = split(rpc) parts = split(rpc)
name = parts.pop(0) name = parts.pop(0)
@ -239,44 +256,39 @@ def parse_rpcs(module):
else: else:
args[key] = str(value) args[key] = str(value)
output = module.params['display'] or 'xml' display = module.params['display'] or 'xml'
items.append({'name': name, 'args': args, 'output': output}) xattrs = {'format': display}
items.append({'name': name, 'args': args, 'xattrs': xattrs})
return items return items
def parse_commands(module): def parse_commands(module):
spec = dict( items = list()
command=dict(key=True),
output=dict(default=module.params['display'], choices=['text', 'json', 'xml']),
prompt=dict(),
answer=dict()
)
transform = ComplexList(spec, module) for command in (module.params['commands'] or list()):
commands = transform(module.params['commands']) if module.check_mode and not command.startswith('show'):
for index, item in enumerate(commands):
if module.check_mode and not item['command'].startswith('show'):
warnings.append( warnings.append(
'Only show commands are supported when using check_mode, not ' 'Only show commands are supported when using check_mode, not '
'executing %s' % item['command'] 'executing %s' % command
) )
if item['command'].startswith('show configuration'): parts = command.split('|')
item['output'] = 'text' text = parts[0]
if item['output'] == 'json' and 'display json' not in item['command']:
item['command'] += '| display json' display = module.params['display'] or 'text'
elif item['output'] == 'xml' and 'display xml' not in item['command']: xattrs = {'format': display}
item['command'] += '| display xml'
else: if '| display json' in command:
if '| display json' in item['command']: xattrs['format'] = 'json'
item['command'] = str(item['command']).replace(' | display json', '')
elif '| display xml' in item['command']: elif '| display xml' in command:
item['command'] = str(item['command']).replace(' | display xml', '') xattrs['format'] = 'xml'
commands[index] = item
items.append({'name': 'command', 'xattrs': xattrs, 'text': text})
return items
return commands
def main(): def main():
"""entry point for module execution """entry point for module execution
@ -296,12 +308,9 @@ def main():
argument_spec.update(junos_argument_spec) argument_spec.update(junos_argument_spec)
mutually_exclusive = [('commands', 'rpcs')]
required_one_of = [('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, required_one_of=required_one_of,
supports_check_mode=True) supports_check_mode=True)
@ -310,11 +319,9 @@ def main():
warnings = list() warnings = list()
check_args(module, warnings) check_args(module, warnings)
if module.params['commands']: items = list()
items = parse_commands(module) items.extend(parse_commands(module))
else: items.extend(parse_rpcs(module))
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']
@ -325,15 +332,12 @@ def main():
match = module.params['match'] match = module.params['match']
while retries > 0: while retries > 0:
if module.params['commands']: responses = rpc(module, items)
responses = run_commands(module, items)
else:
responses = run_rpcs(module, items)
transformed = list() transformed = list()
for item, resp in zip(items, responses): for item, resp in zip(items, responses):
if item['output'] == 'xml': if item['xattrs']['format'] == '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`')