refactor the eos_command module to use the CommandRunner

* This adds support the CommandRunner to handle executing commands on
the remote device.
* It also changes the waitfor argument to wait_for to remain compatable
with other modules and adds an alias for waitfor.
* Restricts commands to show commands only when check mode is specified.
* add version_added to wait_for doc string
This commit is contained in:
Peter Sprygada 2016-06-05 20:34:31 -04:00 committed by Matt Clay
parent a2e6ac6ca8
commit 622592c413

View file

@ -20,7 +20,7 @@ DOCUMENTATION = """
--- ---
module: eos_command module: eos_command
version_added: "2.1" version_added: "2.1"
author: "Peter sprygada (@privateip)" author: "Peter Sprygada (@privateip)"
short_description: Run arbitrary command on EOS device short_description: Run arbitrary command on EOS device
description: description:
- Sends an aribtrary set of commands to an EOS node and returns the results - Sends an aribtrary set of commands to an EOS node and returns the results
@ -37,7 +37,7 @@ options:
module is not returned until the condition is satisfied or module is not returned until the condition is satisfied or
the number of retries has been exceeded. the number of retries has been exceeded.
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,6 +46,8 @@ 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"
retries: retries:
description: description:
- Specifies the number of retries a command should be tried - Specifies the number of retries a command should be tried
@ -65,9 +67,6 @@ options:
""" """
EXAMPLES = """ EXAMPLES = """
- eos_command:
commands: "{{ lookup('file', 'commands.txt') }}"
- eos_command: - eos_command:
commands: commands:
- show interface {{ item }} - show interface {{ item }}
@ -109,14 +108,12 @@ failed_conditions:
type: list type: list
sample: ['...', '...'] sample: ['...', '...']
""" """
from ansible.module_utils.basic import get_exception
from ansible.module_utils.netcmd import CommandRunner, FailedConditionsError
from ansible.module_utils.network import NetworkError
from ansible.module_utils.eos import get_module
import time def to_lines(stdout):
import shlex
import re
INDEX_RE = re.compile(r'(\[\d+\])')
def iterlines(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')
@ -124,8 +121,8 @@ def iterlines(stdout):
def main(): def main():
spec = dict( spec = dict(
commands=dict(type='list'), commands=dict(type='list', required=True),
waitfor=dict(type='list'), wait_for=dict(type='list', aliases=['waitfor']),
retries=dict(default=10, type='int'), retries=dict(default=10, type='int'),
interval=dict(default=1, type='int') interval=dict(default=1, type='int')
) )
@ -134,50 +131,54 @@ def main():
supports_check_mode=True) supports_check_mode=True)
commands = module.params['commands'] commands = module.params['commands']
conditionals = module.params['wait_for'] or list()
retries = module.params['retries'] warnings = list()
interval = module.params['interval']
runner = CommandRunner(module)
for cmd in commands:
if module.check_mode and not cmd.startswith('show'):
warnings.append('only show commands are supported when using '
'check mode, not executing `%s`' % cmd)
else:
runner.add_command(cmd)
for item in conditionals:
runner.add_conditional(item)
runner.retries = module.params['retries']
runner.interval = module.params['interval']
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:
exc = get_exception()
module.fail_json(msg=str(exc))
result = dict(changed=False) result = dict(changed=False)
while retries > 0: result['stdout'] = list()
response = module.execute(commands) for cmd in commands:
result['stdout'] = response try:
output = runner.get_command(cmd)
for index, cmd in enumerate(commands):
if cmd.endswith('json'): if cmd.endswith('json'):
response[index] = module.from_json(response[index]) output = module.from_json(output)
except ValueError:
for item in list(queue): output = 'command not executed due to check_mode, see warnings'
if item(response): result['stdout'].append(output)
queue.remove(item)
if not queue: result['warnings'] = warnings
break result['connected'] = module.connected
result['stdout_lines'] = list(to_lines(result['stdout']))
time.sleep(interval)
retries -= 1 module.exit_json(**result)
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)
from ansible.module_utils.basic import *
from ansible.module_utils.urls import *
from ansible.module_utils.shell import *
from ansible.module_utils.netcfg import *
from ansible.module_utils.eos import *
if __name__ == '__main__': if __name__ == '__main__':
main() main()