Merge pull request #4484 from privateip/nxos_command

added new functionality to nxos_command
reviewable/pr18780/r1
Peter Sprygada 8 years ago committed by GitHub
commit 23f067a0ec

@ -33,11 +33,17 @@ options:
description: description:
- The commands to send to the remote NXOS device over the - The commands to send to the remote NXOS device over the
configured provider. The resulting output from the command configured provider. The resulting output from the command
is returned. If the I(waitfor) 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 retires as expired. the number of retires as expired.
- The I(commands) argument also accepts an alternative form
that allows for complex values that specify the command
to run and the output format to return. This can be done
on a command by command basis. The complex argument supports
the keywords C(command) and C(output) where C(command) is the
command to run and C(output) is one of 'text' or 'json'.
required: true required: true
waitfor: wait_for:
description: description:
- Specifies what to evaluate from the output of the command - Specifies what to evaluate from the output of the command
and what conditionals to apply. This argument will cause and what conditionals to apply. This argument will cause
@ -46,11 +52,24 @@ options:
by the configured retries, the task fails. See examples. by the configured retries, the task fails. See examples.
required: false required: false
default: null default: null
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.
required: false
default: all
version_added: "2.2"
retries: retries:
description: description:
- Specifies the number of retries a command should by tried - Specifies the number of retries a command should by tried
before it is considered failed. The command is run on the before it is considered failed. The command is run on the
target device every retry and evaluated against the I(waitfor) target device every retry and evaluated against the I(wait_for)
conditionals. conditionals.
required: false required: false
default: 10 default: 10
@ -65,33 +84,49 @@ options:
""" """
EXAMPLES = """ EXAMPLES = """
- nxos_command: # Note: examples below use the following provider dict to handle
commands: ["show version"] # transport and authentication to the node.
vars:
- nxos_command: cli:
commands: "{{ lookup('file', 'commands.txt') }}" host: "{{ inventory_hostname }}"
username: admin
- nxos_command: password: admin
transport: cli
- name: run show verion on remote devices
nxos_command:
commands: show version
provider "{{ cli }}"
- name: run show version and check to see if output contains Arista
nxos_command:
commands: show version
wait_for: result[0] contains Cisco
provider: "{{ cli }}"
- name: run multiple commands on remote nodes
nxos_command:
commands: commands:
- "show interface {{ item }}" - show version
with_items: interfaces - show interfaces
provider: "{{ cli }}"
- nxos_command: - name: run multiple commands and evalute the output
nxos_command:
commands: commands:
- show version - show version
waitfor: - show interfaces
- "result[0] contains 7.2(0)D1(1)" wait_for:
- result[0] contains Cisco
- nxos_command: - result[1] contains loopback0
commands: provider: "{{ cli }}"
- show version | json
- show interface Ethernet2/1 | json - name: run commands and specify the output format
- show version nxos_command:
waitfor: commands:
- "result[1].TABLE_interface.ROW_interface.state eq up" - command: show version
- "result[2] contains 'version 7.2(0)D1(1)'" output: json
- "result[0].sys_ver_str == 7.2(0)D1(1)" provider: "{{ cli }}"
""" """
RETURN = """ RETURN = """
@ -113,84 +148,99 @@ failed_conditions:
type: list type: list
sample: ['...', '...'] sample: ['...', '...']
""" """
import time
import re
from ansible.module_utils.basic import get_exception from ansible.module_utils.basic import get_exception
from ansible.module_utils.netcmd import Conditional from ansible.module_utils.netcli import CommandRunner, FailedConditionsError
from ansible.module_utils.network import get_module from ansible.module_utils.netcli import AddCommandError
from ansible.module_utils.nxos import * from ansible.module_utils.nxos import NetworkModule, NetworkError
INDEX_RE = re.compile(r'(\[\d+\])') VALID_KEYS = ['command', 'output', 'prompt', 'response']
def iterlines(stdout): def to_lines(stdout):
for item in stdout: for item in stdout:
if isinstance(item, basestring): if isinstance(item, basestring):
item = str(item).split('\n') item = str(item).split('\n')
yield item yield item
def parse_commands(module):
for cmd in module.params['commands']:
if isinstance(cmd, basestring):
cmd = dict(command=cmd, output=None)
elif 'command' not in cmd:
module.fail_json(msg='command keyword argument is required')
elif cmd.get('output') not in [None, 'text', 'json']:
module.fail_json(msg='invalid output specified for command')
elif not set(cmd.keys()).issubset(VALID_KEYS):
module.fail_json(msg='unknown keyword specified')
yield cmd
def main(): def main():
spec = dict( spec = dict(
commands=dict(type='list'), # { command: <str>, output: <str>, prompt: <str>, response: <str> }
waitfor=dict(type='list'), commands=dict(type='list', required=True),
wait_for=dict(type='list', aliases=['waitfor']),
match=dict(default='all', choices=['any', 'all']),
retries=dict(default=10, type='int'), retries=dict(default=10, type='int'),
interval=dict(default=1, type='int') interval=dict(default=1, type='int')
) )
module = get_module(argument_spec=spec, module = NetworkModule(argument_spec=spec,
supports_check_mode=True) connect_on_load=False,
supports_check_mode=True)
commands = list(parse_commands(module))
conditionals = module.params['wait_for'] or list()
warnings = list()
runner = CommandRunner(module)
for cmd in commands:
if module.check_mode and not cmd['command'].startswith('show'):
warnings.append('only show commands are supported when using '
'check mode, not executing `%s`' % cmd['command'])
else:
if cmd['command'].startswith('conf'):
module.fail_json(msg='nxos_command does not support running '
'config mode commands. Please use '
'nxos_config instead')
try:
runner.add_command(**cmd)
except AddCommandError:
exc = get_exception()
warnings.append('duplicate command detected: %s' % cmd)
commands = module.params['commands'] for item in conditionals:
runner.add_conditional(item)
retries = module.params['retries'] runner.retries = module.params['retries']
interval = module.params['interval'] runner.interval = module.params['interval']
runner.match = module.params['match']
try: try:
queue = set() runner.run()
for entry in (module.params['waitfor'] or list()): except FailedConditionsError:
queue.add(Conditional(entry))
except AttributeError:
exc = get_exception() exc = get_exception()
module.fail_json(msg=exc.message) module.fail_json(msg=str(exc), failed_conditions=exc.failed_conditions)
except NetworkError:
result = dict(changed=False, result=list()) exc = get_exception()
module.fail_json(msg=str(exc))
kwargs = dict()
if module.params['transport'] == 'nxapi': result = dict(changed=False)
kwargs['command_type'] = 'cli_show'
result['stdout'] = list()
while retries > 0: for cmd in commands:
response = module.cli(commands, **kwargs) try:
result['stdout'] = response output = runner.get_command(cmd['command'], cmd.get('output'))
except ValueError:
for index, cmd in enumerate(commands): output = 'command not executed due to check_mode, see warnings'
if cmd.endswith('json'): result['stdout'].append(output)
try:
response[index] = module.from_json(response[index]) result['warnings'] = warnings
except ValueError: result['stdout_lines'] = list(to_lines(result['stdout']))
exc = get_exception()
module.fail_json(msg='failed to parse json response', module.exit_json(**result)
exc_message=str(exc), response=response[index],
cmd=cmd, response_dict=response)
for item in list(queue):
if item(response):
queue.remove(item)
if not queue:
break
time.sleep(interval)
retries -= 1
else:
failed_conditions = [item.raw for item in queue]
module.fail_json(msg='timeout waiting for value', failed_conditions=failed_conditions)
result['stdout_lines'] = list(iterlines(result['stdout']))
return module.exit_json(**result)
if __name__ == '__main__': if __name__ == '__main__':

Loading…
Cancel
Save