Commit 202c4f52 by James Cammarata

Merge branch 'do_until_14' of https://github.com/bennojoy/ansible into bennojoy-do_until_14

parents 7eff2a84 16d03204
...@@ -461,6 +461,39 @@ from turning into arbitrary code with ugly nested ifs, conditionals, and so on - ...@@ -461,6 +461,39 @@ from turning into arbitrary code with ugly nested ifs, conditionals, and so on -
in more streamlined & auditable configuration rules -- especially because there are a in more streamlined & auditable configuration rules -- especially because there are a
minimum of decision points to track. minimum of decision points to track.
Do-Until
````````
Sometimes you would want to retry a task till a certain condition is met, In such conditions the Do/Until feature will help.
Here's an example which show's the syntax to be applied for the task.::
- action: shell /usr/bin/foo
register: result
until: register.stdout.find("all systems go") != -1
retries: 5
delay: 10
The above example run the shell module recursively till the module's result has "all systems go" in it's stdout or the task has
been retried for 5 times with a delay of 10 seconds. The default value for "retries" is 3 and "delay" is 5.
The task returns the results returned by the last task run. The results of individual retries can be viewed by -vv option.
The results will have a new key "attempts" which will have the number of the retries for the task.
.. Note::
The Do/Until does not take decision on whether to fail or pass the play when the maximum retries are completed, the user can
can do that in the next task as follows.
Example::
- action: shell /usr/bin/foo
register: result
until: register.stdout.find("all systems go") != -1
retries: 5
delay: 10
failed_when: result.attempts == 5
Loops Loops
````` `````
......
...@@ -29,7 +29,7 @@ class Task(object): ...@@ -29,7 +29,7 @@ class Task(object):
'delegate_to', 'first_available_file', 'ignore_errors', 'delegate_to', 'first_available_file', 'ignore_errors',
'local_action', 'transport', 'sudo', 'sudo_user', 'sudo_pass', 'local_action', 'transport', 'sudo', 'sudo_user', 'sudo_pass',
'items_lookup_plugin', 'items_lookup_terms', 'environment', 'args', 'items_lookup_plugin', 'items_lookup_terms', 'environment', 'args',
'any_errors_fatal', 'changed_when', 'failed_when', 'always_run' 'any_errors_fatal', 'changed_when', 'failed_when', 'always_run', 'delay', 'retries', 'until'
] ]
# to prevent typos and such # to prevent typos and such
...@@ -38,9 +38,9 @@ class Task(object): ...@@ -38,9 +38,9 @@ class Task(object):
'first_available_file', 'include', 'tags', 'register', 'ignore_errors', 'first_available_file', 'include', 'tags', 'register', 'ignore_errors',
'delegate_to', 'local_action', 'transport', 'sudo', 'sudo_user', 'delegate_to', 'local_action', 'transport', 'sudo', 'sudo_user',
'sudo_pass', 'when', 'connection', 'environment', 'args', 'sudo_pass', 'when', 'connection', 'environment', 'args',
'any_errors_fatal', 'changed_when', 'failed_when', 'always_run' 'any_errors_fatal', 'changed_when', 'failed_when', 'always_run', 'delay', 'retries', 'until'
] ]
def __init__(self, play, ds, module_vars=None, default_vars=None, additional_conditions=None, role_name=None): def __init__(self, play, ds, module_vars=None, default_vars=None, additional_conditions=None, role_name=None):
''' constructor loads from a task or handler datastructure ''' ''' constructor loads from a task or handler datastructure '''
...@@ -111,6 +111,16 @@ class Task(object): ...@@ -111,6 +111,16 @@ class Task(object):
self.sudo = utils.boolean(ds.get('sudo', play.sudo)) self.sudo = utils.boolean(ds.get('sudo', play.sudo))
self.environment = ds.get('environment', {}) self.environment = ds.get('environment', {})
self.role_name = role_name self.role_name = role_name
#Code to allow do until feature in a Task
if 'until' in ds:
if not ds.get('register'):
raise errors.AnsibleError("register keyword is mandatory when using do until feature")
self.module_vars['delay'] = ds.get('delay', 5)
self.module_vars['retries'] = ds.get('retries', 3)
self.module_vars['register'] = ds.get('register', None)
self.until = "jinja2_compare %s" % (ds.get('until'))
self.module_vars['until'] = utils.compile_when_to_only_if(self.until)
# rather than simple key=value args on the options line, these represent structured data and the values # rather than simple key=value args on the options line, these represent structured data and the values
# can be hashes and lists, not just scalars # can be hashes and lists, not just scalars
......
...@@ -658,7 +658,30 @@ class Runner(object): ...@@ -658,7 +658,30 @@ class Runner(object):
result = handler.run(conn, tmp, module_name, module_args, inject, complex_args) result = handler.run(conn, tmp, module_name, module_args, inject, complex_args)
# Code for do until feature
until = self.module_vars.get('until', None)
if until is not None and result.comm_ok:
inject[self.module_vars.get('register')] = result.result
cond = template.template(self.basedir, until, inject, expand_lists=False)
if not utils.check_conditional(cond, self.basedir, inject, fail_on_undefined=self.error_on_undefined_vars):
retries = self.module_vars.get('retries')
delay = self.module_vars.get('delay')
for x in range(1, retries + 1):
time.sleep(delay)
tmp = ''
if getattr(handler, 'NEEDS_TMPPATH', True):
tmp = self._make_tmp_path(conn)
result = handler.run(conn, tmp, module_name, module_args, inject, complex_args)
result.result['attempts'] = x
vv("Result from run %i is: %s" % (x, result.result))
if not result.comm_ok:
break;
inject[self.module_vars.get('register')] = result.result
cond = template.template(self.basedir, until, inject, expand_lists=False)
if utils.check_conditional(cond, self.basedir, inject, fail_on_undefined=self.error_on_undefined_vars):
break;
else:
result.result['attempts'] = 0
conn.close() conn.close()
if not result.comm_ok: if not result.comm_ok:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment