Remove code underlying when_* and only_if, which are deprecated features slated for removal in the 1.5 release.

This commit is contained in:
Michael DeHaan 2014-01-03 19:13:21 -05:00
parent 191be7b951
commit 8e5b7d3095
8 changed files with 87 additions and 191 deletions

View file

@ -3,6 +3,19 @@ Ansible Changes By Release
## 1.5 "Love Walks In" - Release pending! ## 1.5 "Love Walks In" - Release pending!
Major features/changes:
* when_foo which was previously deprecated is now removed, use "when:" instead. Code generates appropriate error suggestion.
* include + with_items which was previously deprecated is now removed, ditto. Use with_nested / with_together, etc.
* only_if, which is much older than when_foo and was deprecated, is similarly removed.
* ssh_alt connection plugin, much more performant than standard -c ssh, in tree, soon to replace ssh.py (in this release)
New modules:
* Details pending
Misc:
* no_reboot is now defaulted to "no" in the ec2_ami module to ensure filesystem consistency in the resulting AMI. * no_reboot is now defaulted to "no" in the ec2_ami module to ensure filesystem consistency in the resulting AMI.
## 1.4.3 "Could This Be Magic" - December 20, 2013 ## 1.4.3 "Could This Be Magic" - December 20, 2013

View file

@ -56,8 +56,8 @@ Conditionals
++++++++++++ ++++++++++++
A conditional is an expression that evaluates to true or false that decides whether a given task will be executed on a given A conditional is an expression that evaluates to true or false that decides whether a given task will be executed on a given
machine or not. Ansible's conditionals include 'when_boolean', machine or not. Ansible's conditionals are powered by the 'when' statement, and are
'when_string', and 'when_integer'. These are discussed in the playbook documentation. discussed in the playbook documentation.
Diff Mode Diff Mode
+++++++++ +++++++++
@ -220,11 +220,6 @@ JSON
Ansible uses JSON for return data from remote modules. This allows modules to be written in any language, not just Python. Ansible uses JSON for return data from remote modules. This allows modules to be written in any language, not just Python.
only_if
+++++++
A deprecated form of the "when:" statement. It should no longer be used.
Library Library
+++++++ +++++++

View file

@ -310,7 +310,7 @@ class PlayBook(object):
remote_port=task.play.remote_port, module_vars=task.module_vars, remote_port=task.play.remote_port, module_vars=task.module_vars,
default_vars=task.default_vars, private_key_file=self.private_key_file, default_vars=task.default_vars, private_key_file=self.private_key_file,
setup_cache=self.SETUP_CACHE, basedir=task.play.basedir, setup_cache=self.SETUP_CACHE, basedir=task.play.basedir,
conditional=task.only_if, callbacks=self.runner_callbacks, conditional=task.when, callbacks=self.runner_callbacks,
sudo=task.sudo, sudo_user=task.sudo_user, sudo=task.sudo, sudo_user=task.sudo_user,
transport=task.transport, sudo_pass=task.sudo_pass, is_playbook=True, transport=task.transport, sudo_pass=task.sudo_pass, is_playbook=True,
check=self.check, diff=self.diff, environment=task.environment, complex_args=task.args, check=self.check, diff=self.diff, environment=task.environment, complex_args=task.args,

View file

@ -489,11 +489,11 @@ class Play(object):
utils.deprecated("\"when_<criteria>:\" is a removed deprecated feature, use the simplified 'when:' conditional directly", None, removed=True) utils.deprecated("\"when_<criteria>:\" is a removed deprecated feature, use the simplified 'when:' conditional directly", None, removed=True)
elif k == 'when': elif k == 'when':
if type(x[k]) is str: if type(x[k]) is str:
included_additional_conditions.insert(0, utils.compile_when_to_only_if("jinja2_compare %s" % x[k])) included_additional_conditions.insert(0, x[k])
elif type(x[k]) is list: elif type(x[k]) is list:
for i in x[k]: for i in x[k]:
included_additional_conditions.insert(0, utils.compile_when_to_only_if("jinja2_compare %s" % i)) included_additional_conditions.insert(0, i)
elif k in ("include", "vars", "default_vars", "only_if", "sudo", "sudo_user", "role_name"): elif k in ("include", "vars", "default_vars", "sudo", "sudo_user", "role_name"):
continue continue
else: else:
include_vars[k] = x[k] include_vars[k] = x[k]
@ -511,8 +511,8 @@ class Play(object):
if 'vars' in x: if 'vars' in x:
task_vars = utils.combine_vars(task_vars, x['vars']) task_vars = utils.combine_vars(task_vars, x['vars'])
if 'only_if' in x: if 'when' in x:
included_additional_conditions.append(x['only_if']) included_additional_conditions.append(x['when'])
new_role = None new_role = None
if 'role_name' in x: if 'role_name' in x:

View file

@ -24,7 +24,7 @@ import sys
class Task(object): class Task(object):
__slots__ = [ __slots__ = [
'name', 'meta', 'action', 'only_if', 'when', 'async_seconds', 'async_poll_interval', 'name', 'meta', 'action', 'when', 'async_seconds', 'async_poll_interval',
'notify', 'module_name', 'module_args', 'module_vars', 'default_vars', 'notify', 'module_name', 'module_args', 'module_vars', 'default_vars',
'play', 'notified_by', 'tags', 'register', 'role_name', 'play', 'notified_by', 'tags', 'register', 'role_name',
'delegate_to', 'first_available_file', 'ignore_errors', 'delegate_to', 'first_available_file', 'ignore_errors',
@ -35,7 +35,7 @@ class Task(object):
# to prevent typos and such # to prevent typos and such
VALID_KEYS = [ VALID_KEYS = [
'name', 'meta', 'action', 'only_if', 'async', 'poll', 'notify', 'name', 'meta', 'action', 'when', 'async', 'poll', 'notify',
'first_available_file', 'include', 'tags', 'register', 'ignore_errors', 'first_available_file', 'include', 'tags', 'register', 'ignore_errors',
'delegate_to', 'local_action', 'transport', 'remote_user', 'sudo', 'sudo_user', 'delegate_to', 'local_action', 'transport', 'remote_user', 'sudo', 'sudo_user',
'sudo_pass', 'when', 'connection', 'environment', 'args', 'sudo_pass', 'when', 'connection', 'environment', 'args',
@ -96,7 +96,6 @@ class Task(object):
elif x in [ 'changed_when', 'failed_when', 'when']: elif x in [ 'changed_when', 'failed_when', 'when']:
if isinstance(ds[x], basestring) and ds[x].lstrip().startswith("{{"): if isinstance(ds[x], basestring) and ds[x].lstrip().startswith("{{"):
utils.warning("It is unneccessary to use '{{' in conditionals, leave variables in loop expressions bare.") utils.warning("It is unneccessary to use '{{' in conditionals, leave variables in loop expressions bare.")
ds[x] = "jinja2_compare %s" % (ds[x])
elif x.startswith("when_"): elif x.startswith("when_"):
utils.deprecated("The 'when_' conditional has been removed. Switch to using the regular unified 'when' statements as described in ansibleworks.com/docs/.","1.5", removed=True) utils.deprecated("The 'when_' conditional has been removed. Switch to using the regular unified 'when' statements as described in ansibleworks.com/docs/.","1.5", removed=True)
@ -128,8 +127,8 @@ class Task(object):
self.module_vars['delay'] = ds.get('delay', 5) self.module_vars['delay'] = ds.get('delay', 5)
self.module_vars['retries'] = ds.get('retries', 3) self.module_vars['retries'] = ds.get('retries', 3)
self.module_vars['register'] = ds.get('register', None) self.module_vars['register'] = ds.get('register', None)
self.until = "jinja2_compare %s" % (ds.get('until')) self.until = ds.get('until')
self.module_vars['until'] = utils.compile_when_to_only_if(self.until) self.module_vars['until'] = 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
@ -188,22 +187,10 @@ class Task(object):
self.name = self.action self.name = self.action
# load various attributes # load various attributes
self.only_if = ds.get('only_if', 'True')
if self.only_if != 'True':
utils.deprecated("only_if is a very old feature and has been obsolete since 0.9, please switch to the 'when' conditional as described at http://ansibleworks.com/docs","1.5",removed=True)
self.when = ds.get('when', None) self.when = ds.get('when', None)
self.changed_when = ds.get('changed_when', None) self.changed_when = ds.get('changed_when', None)
if self.changed_when is not None:
self.changed_when = utils.compile_when_to_only_if(self.changed_when)
self.failed_when = ds.get('failed_when', None) self.failed_when = ds.get('failed_when', None)
if self.failed_when is not None:
self.failed_when = utils.compile_when_to_only_if(self.failed_when)
self.async_seconds = int(ds.get('async', 0)) # not async by default 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.async_poll_interval = int(ds.get('poll', 10)) # default poll = 10 seconds
self.notify = ds.get('notify', []) self.notify = ds.get('notify', [])
@ -276,12 +263,7 @@ class Task(object):
self.tags.extend(apply_tags) self.tags.extend(apply_tags)
self.tags.extend(import_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 = utils.compile_when_to_only_if(self.when)
if additional_conditions: if additional_conditions:
new_conditions = additional_conditions new_conditions = additional_conditions
new_conditions.append(self.only_if) new_conditions.append(self.when)
self.only_if = new_conditions self.when = new_conditions

View file

@ -1067,6 +1067,6 @@ class Runner(object):
if self.always_run is None: if self.always_run is None:
self.always_run = self.module_vars.get('always_run', False) self.always_run = self.module_vars.get('always_run', False)
self.always_run = check_conditional( self.always_run = check_conditional(
self.always_run, self.basedir, inject, fail_on_undefined=True, jinja2=True) self.always_run, self.basedir, inject, fail_on_undefined=True)
return (self.check and not self.always_run) return (self.check and not self.always_run)

View file

@ -163,58 +163,44 @@ def is_changed(result):
return (result.get('changed', False) in [ True, 'True', 'true']) return (result.get('changed', False) in [ True, 'True', 'true'])
def check_conditional(conditional, basedir, inject, fail_on_undefined=False, jinja2=False): def check_conditional(conditional, basedir, inject, fail_on_undefined=False):
if isinstance(conditional, list): if isinstance(conditional, list):
for x in conditional: for x in conditional:
if not check_conditional(x, basedir, inject, fail_on_undefined=fail_on_undefined, jinja2=jinja2): if not check_conditional(x, basedir, inject, fail_on_undefined=fail_on_undefined):
return False return False
return True return True
if jinja2:
conditional = "jinja2_compare %s" % conditional
if conditional.startswith("jinja2_compare"):
conditional = conditional.replace("jinja2_compare ","")
# allow variable names
if conditional in inject and str(inject[conditional]).find('-') == -1:
conditional = inject[conditional]
conditional = template.template(basedir, conditional, inject, fail_on_undefined=fail_on_undefined)
original = str(conditional).replace("jinja2_compare ","")
# a Jinja2 evaluation that results in something Python can eval!
presented = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % conditional
conditional = template.template(basedir, presented, inject)
val = conditional.strip()
if val == presented:
# the templating failed, meaning most likely a
# variable was undefined. If we happened to be
# looking for an undefined variable, return True,
# otherwise fail
if conditional.find("is undefined") != -1:
return True
elif conditional.find("is defined") != -1:
return False
else:
raise errors.AnsibleError("error while evaluating conditional: %s" % original)
elif val == "True":
return True
elif val == "False":
return False
else:
raise errors.AnsibleError("unable to evaluate conditional: %s" % original)
if not isinstance(conditional, basestring): if not isinstance(conditional, basestring):
return conditional return conditional
try: conditional = conditional.replace("jinja2_compare ","")
conditional = conditional.replace("\n", "\\n") # allow variable names
result = safe_eval(conditional) if conditional in inject and str(inject[conditional]).find('-') == -1:
if result not in [ True, False ]: conditional = inject[conditional]
raise errors.AnsibleError("Conditional expression must evaluate to True or False: %s" % conditional) conditional = template.template(basedir, conditional, inject, fail_on_undefined=fail_on_undefined)
return result original = str(conditional).replace("jinja2_compare ","")
# a Jinja2 evaluation that results in something Python can eval!
except (NameError, SyntaxError): presented = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % conditional
raise errors.AnsibleError("Could not evaluate the expression: (%s)" % conditional) conditional = template.template(basedir, presented, inject)
val = conditional.strip()
if val == presented:
# the templating failed, meaning most likely a
# variable was undefined. If we happened to be
# looking for an undefined variable, return True,
# otherwise fail
if conditional.find("is undefined") != -1:
return True
elif conditional.find("is defined") != -1:
return False
else:
raise errors.AnsibleError("error while evaluating conditional: %s" % original)
elif val == "True":
return True
elif val == "False":
return False
else:
raise errors.AnsibleError("unable to evaluate conditional: %s" % original)
def is_executable(path): def is_executable(path):
'''is the given path executable?''' '''is the given path executable?'''
@ -756,86 +742,6 @@ def boolean(value):
else: else:
return False 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: failed $json_result
# when: changed $json_result
# when: int $x >= $z and $y < 3
# when: int $x in $alist
# when: float $x > 2 and $y <= $z
# when: str $x != $y
# when: jinja2_compare asdf # implies {{ asdf }}
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' ]:
tcopy = tokens[1:]
for (i,t) in enumerate(tokens[1:]):
if t.find("$") != -1:
tcopy[i] = "is_%s('''%s''')" % (tokens[0], t)
else:
tcopy[i] = t
return " ".join(tcopy)
# when_failed / when_changed
elif tokens[0] in [ 'failed', 'changed' ]:
tcopy = tokens[1:]
for (i,t) in enumerate(tokens[1:]):
if t.find("$") != -1:
tcopy[i] = "is_%s(%s)" % (tokens[0], t)
else:
tcopy[i] = t
return " ".join(tcopy)
# 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 re.search(t, r"^\w"):
# bare word will turn into Jinja2 so all the above
# casting is really not needed
#tcopy[i] = "%s('''%s''')" % (cast, t)
t2 = t.strip()
if (t2[0].isalpha() or t2[0] == '$') and cast == 'str' and t2 != 'in':
tcopy[i] = "'%s'" % (t)
else:
tcopy[i] = t
result = " ".join(tcopy)
return result
# 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)
# the stock 'when' without qualification (new in 1.2), assumes Jinja2 terms
elif tokens[0] == 'jinja2_compare':
return " ".join(tokens)
else:
raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression)
def make_sudo_cmd(sudo_user, executable, cmd): def make_sudo_cmd(sudo_user, executable, cmd):
""" """
helper function for connection plugins to create sudo commands helper function for connection plugins to create sudo commands

View file

@ -112,27 +112,27 @@ class TestUtils(unittest.TestCase):
# boolean # boolean
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare true', '/', {}) == True) 'true', '/', {}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare false', '/', {}) == False) 'false', '/', {}) == False)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare True', '/', {}) == True) 'True', '/', {}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare False', '/', {}) == False) 'False', '/', {}) == False)
# integer # integer
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare 1', '/', {}) == True) '1', '/', {}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare 0', '/', {}) == False) '0', '/', {}) == False)
# string, beware, a string is truthy unless empty # string, beware, a string is truthy unless empty
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare "yes"', '/', {}) == True) '"yes"', '/', {}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare "no"', '/', {}) == True) '"no"', '/', {}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare ""', '/', {}) == False) '""', '/', {}) == False)
def test_check_conditional_jinja2_variable_literals(self): def test_check_conditional_jinja2_variable_literals(self):
@ -140,61 +140,61 @@ class TestUtils(unittest.TestCase):
# boolean # boolean
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': 'True'}) == True) 'var', '/', {'var': 'True'}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': 'true'}) == True) 'var', '/', {'var': 'true'}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': 'False'}) == False) 'var', '/', {'var': 'False'}) == False)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': 'false'}) == False) 'var', '/', {'var': 'false'}) == False)
# integer # integer
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': '1'}) == True) 'var', '/', {'var': '1'}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': 1}) == True) 'var', '/', {'var': 1}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': '0'}) == False) 'var', '/', {'var': '0'}) == False)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': 0}) == False) 'var', '/', {'var': 0}) == False)
# string, beware, a string is truthy unless empty # string, beware, a string is truthy unless empty
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': '"yes"'}) == True) 'var', '/', {'var': '"yes"'}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': '"no"'}) == True) 'var', '/', {'var': '"no"'}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': '""'}) == False) 'var', '/', {'var': '""'}) == False)
# Python boolean in Jinja2 expression # Python boolean in Jinja2 expression
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': True}) == True) 'var', '/', {'var': True}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': False}) == False) 'var', '/', {'var': False}) == False)
def test_check_conditional_jinja2_expression(self): def test_check_conditional_jinja2_expression(self):
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare 1 == 1', '/', {}) == True) '1 == 1', '/', {}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare bar == 42', '/', {'bar': 42}) == True) 'bar == 42', '/', {'bar': 42}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare bar != 42', '/', {'bar': 42}) == False) 'bar != 42', '/', {'bar': 42}) == False)
def test_check_conditional_jinja2_expression_in_variable(self): def test_check_conditional_jinja2_expression_in_variable(self):
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': '1 == 1'}) == True) 'var', '/', {'var': '1 == 1'}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': 'bar == 42', 'bar': 42}) == True) 'var', '/', {'var': 'bar == 42', 'bar': 42}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
'jinja2_compare var', '/', {'var': 'bar != 42', 'bar': 42}) == False) 'var', '/', {'var': 'bar != 42', 'bar': 42}) == False)
def test_check_conditional_jinja2_unicode(self): def test_check_conditional_jinja2_unicode(self):
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
u'jinja2_compare "\u00df"', '/', {}) == True) u'"\u00df"', '/', {}) == True)
assert(ansible.utils.check_conditional( assert(ansible.utils.check_conditional(
u'jinja2_compare var == "\u00df"', '/', {'var': u'\u00df'}) == True) u'var == "\u00df"', '/', {'var': u'\u00df'}) == True)
##################################### #####################################