Change the way we do with_items to make them happen next to each other in runner, which eliminates the problem of with_items and vars_files sometimes not playing nice with each other.

(Also a fix for the user module error handling when the user
is not present at the time of the return.  This can only really be caused by multiple ansible executions).
pull/4420/head
Michael DeHaan 2012-07-14 19:18:33 -04:00
parent 6c84ec72c6
commit 64c51ade1e
3 changed files with 72 additions and 19 deletions

View File

@ -104,16 +104,14 @@ class Play(object):
for y in data: for y in data:
items = y.get('with_items',None) items = y.get('with_items',None)
if items is None: if items is None:
items = [ '' ] items = [ ]
elif isinstance(items, basestring): elif isinstance(items, basestring):
items = utils.varLookup(items, task_vars) #items = utils.varLookup(items, task_vars)
for item in items: if type(items) != list:
item = utils.template(item, task_vars) raise errors.AnsibleError("with_items must be a list, got: %s" % items)
if self._has_vars_in(item): # items = [ utils.template(item, task_vars) for item in items]
raise errors.AnsibleError("parse error: unbound variable in with_items: %s" % item)
mv = task_vars.copy() mv = task_vars.copy()
mv['item'] = item mv['items'] = items
results.append(Task(self,y,module_vars=mv)) results.append(Task(self,y,module_vars=mv))
for x in results: for x in results:
@ -245,6 +243,8 @@ class Play(object):
if host is not None: if host is not None:
filename3 = utils.template(filename2, self.playbook.SETUP_CACHE[host]) filename3 = utils.template(filename2, self.playbook.SETUP_CACHE[host])
filename4 = utils.path_dwim(self.playbook.basedir, filename3) filename4 = utils.path_dwim(self.playbook.basedir, filename3)
if self._has_vars_in(filename4):
return
new_vars = utils.parse_yaml_from_file(filename4) new_vars = utils.parse_yaml_from_file(filename4)
if new_vars: if new_vars:
if type(new_vars) != dict: if type(new_vars) != dict:

View File

@ -71,13 +71,12 @@ def _executor_hook(job_queue, result_queue):
class ReturnData(object): class ReturnData(object):
__slots__ = [ 'result', 'comm_ok', 'executed_str', 'host' ] __slots__ = [ 'result', 'comm_ok', 'host' ]
def __init__(self, host=None, result=None, comm_ok=True, executed_str=''): def __init__(self, host=None, result=None, comm_ok=True):
self.host = host self.host = host
self.result = result self.result = result
self.comm_ok = comm_ok self.comm_ok = comm_ok
self.executed_str = executed_str
if type(self.result) in [ str, unicode ]: if type(self.result) in [ str, unicode ]:
self.result = utils.parse_json(self.result) self.result = utils.parse_json(self.result)
@ -135,7 +134,7 @@ class Runner(object):
conditional : only execute if this string, evaluated, is True conditional : only execute if this string, evaluated, is True
callbacks : output callback class callbacks : output callback class
sudo : log in as remote user and immediately sudo to root sudo : log in as remote user and immediately sudo to root
module_vars : provides additional variables to a template. FIXME: factor this out module_vars : provides additional variables to a template.
is_playbook : indicates Runner is being used by a playbook. affects behavior in various ways. is_playbook : indicates Runner is being used by a playbook. affects behavior in various ways.
inventory : inventory object, if host_list is not provided inventory : inventory object, if host_list is not provided
""" """
@ -283,6 +282,55 @@ class Runner(object):
def _execute_module(self, conn, tmp, remote_module_path, args, def _execute_module(self, conn, tmp, remote_module_path, args,
async_jid=None, async_module=None, async_limit=None): async_jid=None, async_module=None, async_limit=None):
items = self.module_vars.get('items', None)
if items is None or len(items) == 0:
# executing a single item
return self._execute_module_internal(
conn, tmp, remote_module_path, args,
async_jid=async_jid, async_module=async_module, async_limit=async_limit
)
else:
# executing using with_items, so make multiple calls
# TODO: refactor
aggregrate = {}
all_comm_ok = True
all_changed = False
all_failed = False
results = []
for x in items:
self.module_vars['item'] = x
result = self._execute_module_internal(
conn, tmp, remote_module_path, args,
async_jid=async_jid, async_module=async_module, async_limit=async_limit
)
results.append(result.result)
if result.comm_ok == False:
all_comm_ok = False
break
for x in results:
if x.get('changed') == True:
all_changed = True
if (x.get('failed') == True) or (('rc' in x) and (x['rc'] != 0)):
all_failed = True
break
msg = 'All items succeeded'
if all_failed:
msg = "One or more items failed."
rd_result = dict(
failed = all_failed,
changed = all_changed,
results = results,
msg = msg
)
if not all_failed:
del rd_result['failed']
return ReturnData(host=conn.host, comm_ok=all_comm_ok, result=rd_result)
# *****************************************************
def _execute_module_internal(self, conn, tmp, remote_module_path, args,
async_jid=None, async_module=None, async_limit=None):
''' runs a module that has already been transferred ''' ''' runs a module that has already been transferred '''
inject = self.setup_cache.get(conn.host,{}).copy() inject = self.setup_cache.get(conn.host,{}).copy()
@ -304,6 +352,7 @@ class Runner(object):
if type(args) == dict: if type(args) == dict:
args = utils.bigjson(args) args = utils.bigjson(args)
args = utils.template(args, inject, self.setup_cache) args = utils.template(args, inject, self.setup_cache)
module_name_tail = remote_module_path.split("/")[-1] module_name_tail = remote_module_path.split("/")[-1]
@ -316,9 +365,7 @@ class Runner(object):
res = self._low_level_exec_command(conn, cmd, tmp, sudoable=True) res = self._low_level_exec_command(conn, cmd, tmp, sudoable=True)
executed_str = "%s %s" % (module_name_tail, args.strip()) return ReturnData(host=conn.host, result=res)
return ReturnData(host=conn.host, result=res, executed_str=executed_str)
# ***************************************************** # *****************************************************
@ -597,7 +644,6 @@ class Runner(object):
# modify file attribs if needed # modify file attribs if needed
if exec_rc.comm_ok: if exec_rc.comm_ok:
exec_rc.executed_str = exec_rc.executed_str.replace("copy","template",1)
return self._chain_file_module(conn, tmp, exec_rc, options) return self._chain_file_module(conn, tmp, exec_rc, options)
else: else:
return exec_rc return exec_rc

View File

@ -54,6 +54,13 @@ def add_user_info(kwargs):
if user_exists(name): if user_exists(name):
kwargs['state'] = 'present' kwargs['state'] = 'present'
info = user_info(name) info = user_info(name)
if info == False:
if 'failed' in kwargs:
kwargs['notice'] = "failed to look up user name: %s" % name
else:
kwargs['msg'] = "failed to look up user name: %s" % name
kwargs['failed'] = True
return kwargs
kwargs['uid'] = info[2] kwargs['uid'] = info[2]
kwargs['group'] = info[3] kwargs['group'] = info[3]
kwargs['comment'] = info[4] kwargs['comment'] = info[4]