|
|
@ -37,8 +37,16 @@ from ansible.utils.listify import listify_lookup_plugin_terms
|
|
|
|
from ansible.utils.unicode import to_unicode
|
|
|
|
from ansible.utils.unicode import to_unicode
|
|
|
|
from ansible.vars.unsafe_proxy import UnsafeProxy
|
|
|
|
from ansible.vars.unsafe_proxy import UnsafeProxy
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
from __main__ import display
|
|
|
|
|
|
|
|
display = display
|
|
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
|
|
|
from ansible.utils.display import Display
|
|
|
|
|
|
|
|
display = Display()
|
|
|
|
|
|
|
|
|
|
|
|
__all__ = ['TaskExecutor']
|
|
|
|
__all__ = ['TaskExecutor']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TaskExecutor:
|
|
|
|
class TaskExecutor:
|
|
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
'''
|
|
|
@ -61,20 +69,13 @@ class TaskExecutor:
|
|
|
|
self._loader = loader
|
|
|
|
self._loader = loader
|
|
|
|
self._shared_loader_obj = shared_loader_obj
|
|
|
|
self._shared_loader_obj = shared_loader_obj
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
from __main__ import display
|
|
|
|
|
|
|
|
self._display = display
|
|
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
|
|
|
from ansible.utils.display import Display
|
|
|
|
|
|
|
|
self._display = Display()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
def run(self):
|
|
|
|
'''
|
|
|
|
'''
|
|
|
|
The main executor entrypoint, where we determine if the specified
|
|
|
|
The main executor entrypoint, where we determine if the specified
|
|
|
|
task requires looping and either runs the task with
|
|
|
|
task requires looping and either runs the task with
|
|
|
|
'''
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
self._display.debug("in run()")
|
|
|
|
display.debug("in run()")
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
# lookup plugins need to know if this task is executing from
|
|
|
|
# lookup plugins need to know if this task is executing from
|
|
|
@ -95,9 +96,9 @@ class TaskExecutor:
|
|
|
|
failed = False
|
|
|
|
failed = False
|
|
|
|
for item in item_results:
|
|
|
|
for item in item_results:
|
|
|
|
if 'changed' in item and item['changed']:
|
|
|
|
if 'changed' in item and item['changed']:
|
|
|
|
changed = True
|
|
|
|
changed = True
|
|
|
|
if 'failed' in item and item['failed']:
|
|
|
|
if 'failed' in item and item['failed']:
|
|
|
|
failed = True
|
|
|
|
failed = True
|
|
|
|
|
|
|
|
|
|
|
|
# create the overall result item, and set the changed/failed
|
|
|
|
# create the overall result item, and set the changed/failed
|
|
|
|
# flags there to reflect the overall result of the loop
|
|
|
|
# flags there to reflect the overall result of the loop
|
|
|
@ -114,9 +115,9 @@ class TaskExecutor:
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
res = dict(changed=False, skipped=True, skipped_reason='No items in the list', results=[])
|
|
|
|
res = dict(changed=False, skipped=True, skipped_reason='No items in the list', results=[])
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
self._display.debug("calling self._execute()")
|
|
|
|
display.debug("calling self._execute()")
|
|
|
|
res = self._execute()
|
|
|
|
res = self._execute()
|
|
|
|
self._display.debug("_execute() done")
|
|
|
|
display.debug("_execute() done")
|
|
|
|
|
|
|
|
|
|
|
|
# make sure changed is set in the result, if it's not present
|
|
|
|
# make sure changed is set in the result, if it's not present
|
|
|
|
if 'changed' not in res:
|
|
|
|
if 'changed' not in res:
|
|
|
@ -133,9 +134,9 @@ class TaskExecutor:
|
|
|
|
return res._obj
|
|
|
|
return res._obj
|
|
|
|
return res
|
|
|
|
return res
|
|
|
|
|
|
|
|
|
|
|
|
self._display.debug("dumping result to json")
|
|
|
|
display.debug("dumping result to json")
|
|
|
|
res = _clean_res(res)
|
|
|
|
res = _clean_res(res)
|
|
|
|
self._display.debug("done dumping result, returning")
|
|
|
|
display.debug("done dumping result, returning")
|
|
|
|
return res
|
|
|
|
return res
|
|
|
|
except AnsibleError as e:
|
|
|
|
except AnsibleError as e:
|
|
|
|
return dict(failed=True, msg=to_unicode(e, nonstring='simplerepr'))
|
|
|
|
return dict(failed=True, msg=to_unicode(e, nonstring='simplerepr'))
|
|
|
@ -145,7 +146,7 @@ class TaskExecutor:
|
|
|
|
except AttributeError:
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
except Exception as e:
|
|
|
|
except Exception as e:
|
|
|
|
self._display.debug("error closing connection: %s" % to_unicode(e))
|
|
|
|
display.debug("error closing connection: %s" % to_unicode(e))
|
|
|
|
|
|
|
|
|
|
|
|
def _get_loop_items(self):
|
|
|
|
def _get_loop_items(self):
|
|
|
|
'''
|
|
|
|
'''
|
|
|
@ -166,23 +167,26 @@ class TaskExecutor:
|
|
|
|
items = None
|
|
|
|
items = None
|
|
|
|
if self._task.loop:
|
|
|
|
if self._task.loop:
|
|
|
|
if self._task.loop in self._shared_loader_obj.lookup_loader:
|
|
|
|
if self._task.loop in self._shared_loader_obj.lookup_loader:
|
|
|
|
#TODO: remove convert_bare true and deprecate this in with_
|
|
|
|
#TODO: remove convert_bare true and deprecate this in with_
|
|
|
|
if self._task.loop == 'first_found':
|
|
|
|
if self._task.loop == 'first_found':
|
|
|
|
# first_found loops are special. If the item is undefined
|
|
|
|
# first_found loops are special. If the item is undefined
|
|
|
|
# then we want to fall through to the next value rather
|
|
|
|
# then we want to fall through to the next value rather
|
|
|
|
# than failing.
|
|
|
|
# than failing.
|
|
|
|
loop_terms = listify_lookup_plugin_terms(terms=self._task.loop_args, templar=templar, loader=self._loader, fail_on_undefined=False, convert_bare=True)
|
|
|
|
loop_terms = listify_lookup_plugin_terms(terms=self._task.loop_args, templar=templar,
|
|
|
|
|
|
|
|
loader=self._loader, fail_on_undefined=False, convert_bare=True)
|
|
|
|
loop_terms = [t for t in loop_terms if not templar._contains_vars(t)]
|
|
|
|
loop_terms = [t for t in loop_terms if not templar._contains_vars(t)]
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
loop_terms = listify_lookup_plugin_terms(terms=self._task.loop_args, templar=templar, loader=self._loader, fail_on_undefined=True, convert_bare=True)
|
|
|
|
loop_terms = listify_lookup_plugin_terms(terms=self._task.loop_args, templar=templar,
|
|
|
|
|
|
|
|
loader=self._loader, fail_on_undefined=True, convert_bare=True)
|
|
|
|
except AnsibleUndefinedVariable as e:
|
|
|
|
except AnsibleUndefinedVariable as e:
|
|
|
|
if 'has no attribute' in str(e):
|
|
|
|
if 'has no attribute' in str(e):
|
|
|
|
loop_terms = []
|
|
|
|
loop_terms = []
|
|
|
|
self._display.deprecated("Skipping task due to undefined attribute, in the future this will be a fatal error.")
|
|
|
|
display.deprecated("Skipping task due to undefined attribute, in the future this will be a fatal error.")
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
raise
|
|
|
|
raise
|
|
|
|
items = self._shared_loader_obj.lookup_loader.get(self._task.loop, loader=self._loader, templar=templar).run(terms=loop_terms, variables=vars_copy)
|
|
|
|
items = self._shared_loader_obj.lookup_loader.get(self._task.loop, loader=self._loader,
|
|
|
|
|
|
|
|
templar=templar).run(terms=loop_terms, variables=vars_copy)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
raise AnsibleError("Unexpected failure in finding the lookup named '%s' in the available lookup plugins" % self._task.loop)
|
|
|
|
raise AnsibleError("Unexpected failure in finding the lookup named '%s' in the available lookup plugins" % self._task.loop)
|
|
|
|
|
|
|
|
|
|
|
@ -313,7 +317,7 @@ class TaskExecutor:
|
|
|
|
# variable not being present which would otherwise cause validation to fail
|
|
|
|
# variable not being present which would otherwise cause validation to fail
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
if not self._task.evaluate_conditional(templar, variables):
|
|
|
|
if not self._task.evaluate_conditional(templar, variables):
|
|
|
|
self._display.debug("when evaluation failed, skipping this task")
|
|
|
|
display.debug("when evaluation failed, skipping this task")
|
|
|
|
return dict(changed=False, skipped=True, skip_reason='Conditional check failed', _ansible_no_log=self._play_context.no_log)
|
|
|
|
return dict(changed=False, skipped=True, skip_reason='Conditional check failed', _ansible_no_log=self._play_context.no_log)
|
|
|
|
except AnsibleError:
|
|
|
|
except AnsibleError:
|
|
|
|
# skip conditional exception in the case of includes as the vars needed might not be avaiable except in the included tasks or due to tags
|
|
|
|
# skip conditional exception in the case of includes as the vars needed might not be avaiable except in the included tasks or due to tags
|
|
|
@ -340,7 +344,7 @@ class TaskExecutor:
|
|
|
|
if '_variable_params' in self._task.args:
|
|
|
|
if '_variable_params' in self._task.args:
|
|
|
|
variable_params = self._task.args.pop('_variable_params')
|
|
|
|
variable_params = self._task.args.pop('_variable_params')
|
|
|
|
if isinstance(variable_params, dict):
|
|
|
|
if isinstance(variable_params, dict):
|
|
|
|
self._display.deprecated("Using variables for task params is unsafe, especially if the variables come from an external source like facts")
|
|
|
|
display.deprecated("Using variables for task params is unsafe, especially if the variables come from an external source like facts")
|
|
|
|
variable_params.update(self._task.args)
|
|
|
|
variable_params.update(self._task.args)
|
|
|
|
self._task.args = variable_params
|
|
|
|
self._task.args = variable_params
|
|
|
|
|
|
|
|
|
|
|
@ -372,19 +376,19 @@ class TaskExecutor:
|
|
|
|
#vars_copy = variables.copy()
|
|
|
|
#vars_copy = variables.copy()
|
|
|
|
vars_copy = variables
|
|
|
|
vars_copy = variables
|
|
|
|
|
|
|
|
|
|
|
|
self._display.debug("starting attempt loop")
|
|
|
|
display.debug("starting attempt loop")
|
|
|
|
result = None
|
|
|
|
result = None
|
|
|
|
for attempt in range(retries):
|
|
|
|
for attempt in range(retries):
|
|
|
|
if attempt > 0:
|
|
|
|
if attempt > 0:
|
|
|
|
self._display.display("FAILED - RETRYING: %s (%d retries left). Result was: %s" % (self._task, retries-attempt, result), color="red")
|
|
|
|
display.display("FAILED - RETRYING: %s (%d retries left). Result was: %s" % (self._task, retries-attempt, result), color="red")
|
|
|
|
result['attempts'] = attempt + 1
|
|
|
|
result['attempts'] = attempt + 1
|
|
|
|
|
|
|
|
|
|
|
|
self._display.debug("running the handler")
|
|
|
|
display.debug("running the handler")
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
result = self._handler.run(task_vars=variables)
|
|
|
|
result = self._handler.run(task_vars=variables)
|
|
|
|
except AnsibleConnectionFailure as e:
|
|
|
|
except AnsibleConnectionFailure as e:
|
|
|
|
return dict(unreachable=True, msg=str(e))
|
|
|
|
return dict(unreachable=True, msg=str(e))
|
|
|
|
self._display.debug("handler run complete")
|
|
|
|
display.debug("handler run complete")
|
|
|
|
|
|
|
|
|
|
|
|
if self._task.async > 0:
|
|
|
|
if self._task.async > 0:
|
|
|
|
# the async_wrapper module returns dumped JSON via its stdout
|
|
|
|
# the async_wrapper module returns dumped JSON via its stdout
|
|
|
@ -479,7 +483,7 @@ class TaskExecutor:
|
|
|
|
result["_ansible_no_log"] = self._play_context.no_log
|
|
|
|
result["_ansible_no_log"] = self._play_context.no_log
|
|
|
|
|
|
|
|
|
|
|
|
# and return
|
|
|
|
# and return
|
|
|
|
self._display.debug("attempt loop complete, returning result")
|
|
|
|
display.debug("attempt loop complete, returning result")
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def _poll_async_result(self, result, templar):
|
|
|
|
def _poll_async_result(self, result, templar):
|
|
|
@ -632,4 +636,3 @@ class TaskExecutor:
|
|
|
|
raise AnsibleError("the handler '%s' was not found" % handler_name)
|
|
|
|
raise AnsibleError("the handler '%s' was not found" % handler_name)
|
|
|
|
|
|
|
|
|
|
|
|
return handler
|
|
|
|
return handler
|
|
|
|
|
|
|
|
|
|
|
|