diff --git a/lib/ansible/modules/network/junos/junos_command.py b/lib/ansible/modules/network/junos/junos_command.py index edbd36c8947..2a332551150 100644 --- a/lib/ansible/modules/network/junos/junos_command.py +++ b/lib/ansible/modules/network/junos/junos_command.py @@ -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`')