diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index 87e993722cf..06cf184c1e8 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -97,7 +97,7 @@ class Play(object): # ************************************************* - def _load_tasks(self, tasks, vars={}): + def _load_tasks(self, tasks, vars={}, additional_conditions=[]): ''' handle task and handler include statements ''' results = [] @@ -107,20 +107,25 @@ class Play(object): if 'include' in x: tokens = shlex.split(x['include']) items = [''] + included_additional_conditions = list(additional_conditions) for k in x: - if not k.startswith("with_"): - if k in ("include", "vars"): - continue - else: - raise errors.AnsibleError("parse error: task includes cannot be used with other directives: %s" % k) - plugin_name = k[5:] - if plugin_name not in utils.plugins.lookup_loader: - raise errors.AnsibleError("cannot find lookup plugin named %s for usage in with_%s" % (plugin_name, plugin_name)) - terms = utils.template_ds(self.basedir, x[k], task_vars) - items = utils.plugins.lookup_loader.get(plugin_name, basedir=self.basedir, runner=None).run(terms, inject=task_vars) + if k.startswith("with_"): + plugin_name = k[5:] + if plugin_name not in utils.plugins.lookup_loader: + raise errors.AnsibleError("cannot find lookup plugin named %s for usage in with_%s" % (plugin_name, plugin_name)) + terms = utils.template_ds(self.basedir, x[k], task_vars) + items = utils.plugins.lookup_loader.get(plugin_name, basedir=self.basedir, runner=None).run(terms, inject=task_vars) + elif k.startswith("when_"): + included_additional_conditions.append(utils.compile_when_to_only_if("%s %s" % (k[5:], x[k]))) + elif k in ("include", "vars", "only_if"): + pass + else: + raise errors.AnsibleError("parse error: task includes cannot be used with other directives: %s" % k) if 'vars' in x: task_vars.update(x['vars']) + if 'only_if' in x: + included_additional_conditions.append(x['only_if']) for item in items: mv = task_vars.copy() @@ -130,9 +135,9 @@ class Play(object): mv[k] = utils.template_ds(self.basedir, v, mv) include_file = utils.template(self.basedir, tokens[0], mv) data = utils.parse_yaml_from_file(utils.path_dwim(self.basedir, include_file)) - results += self._load_tasks(data, mv) + results += self._load_tasks(data, mv, included_additional_conditions) elif type(x) == dict: - results.append(Task(self,x,module_vars=task_vars)) + results.append(Task(self,x,module_vars=task_vars, additional_conditions=additional_conditions)) else: raise Exception("unexpected task type") diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 119bec0ed13..e7bed0acd9d 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -38,7 +38,7 @@ class Task(object): 'sudo_pass', 'when' ] - def __init__(self, play, ds, module_vars=None): + def __init__(self, play, ds, module_vars=None, additional_conditions=None): ''' constructor loads from a task or handler datastructure ''' for x in ds.keys(): @@ -182,63 +182,8 @@ class Task(object): if self.when is not None: if self.only_if != 'True': raise errors.AnsibleError('when obsoletes only_if, only use one or the other') - self.only_if = self.compile_when_to_only_if(self.when) - - def compile_when_to_only_if(self, expression): - ''' - when is a shorthand for writing only_if conditionals. It requires less quoting - magic. only_if is retained for backwards compatibility. - ''' - - # when: set $variable - # when: unset $variable - # when: int $x >= $z and $y < 3 - # when: int $x in $alist - # when: float $x > 2 and $y <= $z - # when: str $x != $y - - if type(expression) not in [ str, unicode ]: - raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression) - tokens = expression.split() - if len(tokens) < 2: - raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression) - - # when_set / when_unset - if tokens[0] in [ 'set', 'unset' ]: - if len(tokens) != 2: - raise errors.AnsibleError("usage: when: <$variableName>") - return "is_%s('''%s''')" % (tokens[0], tokens[1]) - - # when_integer / when_float / when_string - elif tokens[0] in [ 'integer', 'float', 'string' ]: - cast = None - if tokens[0] == 'integer': - cast = 'int' - elif tokens[0] == 'string': - cast = 'str' - elif tokens[0] == 'float': - cast = 'float' - tcopy = tokens[1:] - for (i,t) in enumerate(tokens[1:]): - if t.find("$") != -1: - # final variable substitution will happen in Runner code - tcopy[i] = "%s('''%s''')" % (cast, t) - else: - tcopy[i] = t - return " ".join(tcopy) - - # when_boolean - elif tokens[0] in [ 'bool', 'boolean' ]: - tcopy = tokens[1:] - for (i, t) in enumerate(tcopy): - if t.find("$") != -1: - tcopy[i] = "(is_set('''%s''') and '''%s'''.lower() not in ('false', 'no', 'n', 'none', '0', ''))" % (t, t) - return " ".join(tcopy) - - else: - raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression) - - - + self.only_if = utils.compile_when_to_only_if(self.when) + if additional_conditions: + self.only_if = '(' + self.only_if + ') and (' + ' ) and ('.join(additional_conditions) + ')' diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index e71351b7276..27b437c76e1 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -476,4 +476,57 @@ def boolean(value): else: return False +def compile_when_to_only_if(expression): + ''' + when is a shorthand for writing only_if conditionals. It requires less quoting + magic. only_if is retained for backwards compatibility. + ''' + + # when: set $variable + # when: unset $variable + # when: int $x >= $z and $y < 3 + # when: int $x in $alist + # when: float $x > 2 and $y <= $z + # when: str $x != $y + + if type(expression) not in [ str, unicode ]: + raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression) + tokens = expression.split() + if len(tokens) < 2: + raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression) + + # when_set / when_unset + if tokens[0] in [ 'set', 'unset' ]: + if len(tokens) != 2: + raise errors.AnsibleError("usage: when: <$variableName>") + return "is_%s('''%s''')" % (tokens[0], tokens[1]) + + # when_integer / when_float / when_string + elif tokens[0] in [ 'integer', 'float', 'string' ]: + cast = None + if tokens[0] == 'integer': + cast = 'int' + elif tokens[0] == 'string': + cast = 'str' + elif tokens[0] == 'float': + cast = 'float' + tcopy = tokens[1:] + for (i,t) in enumerate(tokens[1:]): + if t.find("$") != -1: + # final variable substitution will happen in Runner code + tcopy[i] = "%s('''%s''')" % (cast, t) + else: + tcopy[i] = t + return " ".join(tcopy) + + # when_boolean + elif tokens[0] in [ 'bool', 'boolean' ]: + tcopy = tokens[1:] + for (i, t) in enumerate(tcopy): + if t.find("$") != -1: + tcopy[i] = "(is_set('''%s''') and '''%s'''.lower() not in ('false', 'no', 'n', 'none', '0', ''))" % (t, t) + return " ".join(tcopy) + + else: + raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression)