diff --git a/examples/playbooks/conditionals_part2.yml b/examples/playbooks/conditionals_part2.yml index fece122256..64b3766be6 100644 --- a/examples/playbooks/conditionals_part2.yml +++ b/examples/playbooks/conditionals_part2.yml @@ -1,47 +1,50 @@ --- -# this is a demo of conditional executions using 'only_if', which can skip -# certain tasks on machines/platforms/etc where they do not apply. +# this is a demo of conditional executions using 'when' statements, which can skip +# certain tasks on machines/platforms/etc where they do not apply. - hosts: all user: root vars: favcolor: "red" + dog: "fido" + cat: "whiskers" ssn: 8675309 -# Below we're going to define some expressions. +# These are the types of when statemnets available +# when_set: $variable_name +# when_unset: $variable_name +# when_str: $x == "test" +# when_int: $y > 2 +# when_float: $z => 2.3 # -# Not only can we assign variables for reuse, but we can also assign conditional -# expressions. By keeping these in 'vars', the task section remains -# extraordinarily clean, and not littered with programming language -# constructs -- so it's easily skimmed by humans. -# -# Remember to quote any variables if they are not numbers! -# -# Interesting fact: aside from the $variables, these expressions are actually -# tiny bits of Python. They are evaluated in the context of each host, so different -# steps can be skipped on different hosts! They should evaluate to either True -# or False - - is_favcolor_blue: "'$favcolor' == 'blue'" - is_centos: "'$facter_operatingsystem' == 'CentOS'" - -# NOTE: -# -# facter and ohai variables can be used in only_if statements too -# ex: "'$facter_operatingsystem' == 'CentOS'", which bubble up automatically -# from the managed machines -# -# this example won't do that though, as you might not have facter or ohai, -# but you get the idea... +# when using 'when', take care to make sure any variables given are surrounded by spaces +# as an example, $z>3 will not do what you want, use "$z > 3" tasks: - - name: "do this if my favcolor is blue" + - name: "do this if my favcolor is blue, and my dog is named fido" action: shell /bin/false - only_if: '$is_favcolor_blue' + when_string: $favcolor == 'blue' and $dog == 'fido' - - name: "do this if my favcolor is not blue" + - name: "do this if my favcolor is not blue, and my dog is named fido" action: shell /bin/true - only_if: 'not ($is_favcolor_blue)' + when_string: $favcolor != 'blue' and $dog == 'fido' + + - name: "do this if my SSN is over 9000" + action: shell /bin/true + when_integer: $ssn > 9000 + + - name: "do this if I have one of these SSNs" + action: shell /bin/true + when_integer: $ssn in [ 8675309, 8675310, 8675311 ] + + - name: "do this if a variable named hippo is NOT defined" + action: shell /bin/true + when_unset: $hippo + + - name: "do this if a variable named hippo is defined" + action: shell /bin/true + when_set: $hippo + diff --git a/examples/playbooks/conditionals_part3.yml b/examples/playbooks/conditionals_part3.yml new file mode 100644 index 0000000000..30306ef3e1 --- /dev/null +++ b/examples/playbooks/conditionals_part3.yml @@ -0,0 +1,47 @@ +--- +# this is a demo of conditional executions using 'only_if', which can skip +# certain tasks on machines/platforms/etc where they do not apply. This is +# the more 'raw' version of the 'when' statement, most users will be able to +# use 'when' directly. 'only_if' is an older feature, and useful for when +# you need more advanced expression control. + +- hosts: all + user: root + + vars: + favcolor: "red" + ssn: 8675309 + +# Below we're going to define some expressions. +# +# Not only can we assign variables for reuse, but we can also assign conditional +# expressions. By keeping these in 'vars', the task section remains +# extraordinarily clean, and not littered with programming language +# constructs -- so it's easily skimmed by humans. +# +# Remember to quote any variables if they are not numbers! +# +# Interesting fact: aside from the $variables, these expressions are actually +# tiny bits of Python. They are evaluated in the context of each host, so different +# steps can be skipped on different hosts! They should evaluate to either True +# or False + + is_favcolor_blue: "'$favcolor' == 'blue'" + is_centos: "'$facter_operatingsystem' == 'CentOS'" + +# NOTE: +# +# setup module values, facter and ohai variables can be used in only_if statements too +# ex: "'$facter_operatingsystem' == 'CentOS'", which bubble up automatically +# from the managed machines. This example doesn't do that though. + + tasks: + + - name: "do this if my favcolor is blue" + action: shell /bin/false + only_if: '$is_favcolor_blue' + + - name: "do this if my favcolor is not blue" + action: shell /bin/true + only_if: 'not ($is_favcolor_blue)' + diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 9a2e39f139..81db27733d 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -22,7 +22,7 @@ from ansible import utils class Task(object): __slots__ = [ - 'name', 'action', 'only_if', 'async_seconds', 'async_poll_interval', + 'name', 'action', 'only_if', 'when', 'async_seconds', 'async_poll_interval', 'notify', 'module_name', 'module_args', 'module_vars', 'play', 'notified_by', 'tags', 'register', 'delegate_to', 'first_available_file', 'ignore_errors', @@ -58,6 +58,11 @@ class Task(object): else: raise errors.AnsibleError("cannot find lookup plugin named %s for usage in with_%s" % (plugin_name, plugin_name)) + elif x.startswith("when_"): + when_name = x.replace("when_","") + ds['when'] = "%s %s" % (when_name, ds[x]) + ds.pop(x) + elif not x in Task.VALID_KEYS: raise errors.AnsibleError("%s is not a legal parameter in an Ansible task or handler" % x) @@ -109,6 +114,8 @@ class Task(object): # load various attributes self.only_if = ds.get('only_if', 'True') + self.when = ds.get('when', None) + self.async_seconds = int(ds.get('async', 0)) # not async by default self.async_poll_interval = int(ds.get('poll', 10)) # default poll = 10 seconds self.notify = ds.get('notify', []) @@ -169,3 +176,58 @@ class Task(object): self.tags.extend(apply_tags) self.tags.extend(import_tags) + 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) + + else: + raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression) + + + + +