Upgraded variable support met with upgraded conditional support, see examples/playbooks/upgraded_vars.yml

for details!
This commit is contained in:
Michael DeHaan 2013-04-05 19:10:32 -04:00
parent 38c2b14339
commit f585c4cde7
6 changed files with 58 additions and 25 deletions

View file

@ -8,7 +8,8 @@ Core Features:
* capability to set 'all_errors_fatal: True' in a playbook to force any error to stop execution versus
a whole group or serial block needing to fail
usable, without breaking the ability to override in ansible
* ability to use variables from {{ }} syntax in mainline playbooks (work in progress)
* ability to use variables from {{ }} syntax in mainline playbooks, new 'when' conditional,
see examples/playbooks/upgraded_vars.yml
* can set ansible_private_key_file as an inventory variable (similar to ansible_ssh_host, etc)
Modules added

View file

@ -0,0 +1,24 @@
# in Ansible 1.2 and later, the $foo variable syntax, which is friendly enough for simple things
# has been upgraded to allow Jinja2 substitiutions as well, which is now the preferred Syntax.
# here is an example. Note that Jinja2 conditionals belong only in templates. Use ansible conditionals
# in playbooks.
---
- hosts: all
tasks:
- shell: echo 'hello {{ ansible_hostname.upper() }}'
- shell: echo 'match'
when: 2 == 2
- shell: echo 'no match'
when: 2 == 2 + 1
- shell: echo '{{ ansible_os_family }}'
- shell: echo 'Centos'
when: ansible_os_family == 'RedHat'

View file

@ -71,6 +71,8 @@ class Task(object):
else:
raise errors.AnsibleError("cannot find lookup plugin named %s for usage in with_%s" % (plugin_name, plugin_name))
elif x == 'when':
ds['when'] = "jinja2_compare %s" % (ds[x])
elif x.startswith("when_"):
if 'when' in ds:
raise errors.AnsibleError("multiple when_* statements specified in task %s" % (ds.get('name', ds['action'])))

View file

@ -424,6 +424,7 @@ class Runner(object):
handler = utils.plugins.action_loader.get('async', self)
conditional = utils.template(self.basedir, self.conditional, inject, expand_lists=False)
if not utils.check_conditional(conditional):
result = utils.jsonify(dict(skipped=True))
self.callbacks.on_skipped(host, inject.get('item',None))

View file

@ -155,13 +155,18 @@ def check_conditional(conditional):
return conditional
def is_set(var):
return not var.startswith("$")
return not var.startswith("$") and not '{{' in var
def is_unset(var):
return var.startswith("$")
return var.startswith("$") or '{{' in var
try:
return eval(conditional.replace("\n", "\\n"))
conditional = conditional.replace("\n", "\\n")
result = eval(conditional)
if result not in [ True, False ]:
raise errors.AnsibleError("Conditional expression must evaluate to True or False: %s" % conditional)
return result
except (NameError, SyntaxError):
raise errors.AnsibleError("Could not evaluate the expression: " + conditional)
@ -545,6 +550,7 @@ def compile_when_to_only_if(expression):
# 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)
@ -562,8 +568,6 @@ def compile_when_to_only_if(expression):
tcopy[i] = t
return " ".join(tcopy)
# when_failed / when_changed
elif tokens[0] in [ 'failed', 'changed' ]:
tcopy = tokens[1:]
@ -574,8 +578,6 @@ def compile_when_to_only_if(expression):
tcopy[i] = t
return " ".join(tcopy)
# when_integer / when_float / when_string
elif tokens[0] in [ 'integer', 'float', 'string' ]:
cast = None
@ -602,6 +604,11 @@ def compile_when_to_only_if(expression):
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':
# a Jinja2 evaluation that results in something Python can eval!
presented = "{% if " + " ".join(tokens[1:]).strip() + " %} True {% else %} False {% endif %}"
return presented
else:
raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression)

View file

@ -232,7 +232,7 @@ def template(basedir, varname, vars, lookup_fatal=True, depth=0, expand_lists=Tr
''' templates a data structure by traversing it and substituting for other data structures '''
if isinstance(varname, basestring):
if '{{' in varname:
if '{{' in varname or '{%' in varname:
return template_from_string(basedir, varname, vars)
m = _varFind(basedir, varname, vars, lookup_fatal, depth, expand_lists)
if not m:
@ -403,17 +403,13 @@ def _get_filter_plugins():
FILTER_PLUGINS.update(filters)
return FILTER_PLUGINS
class J2Undefined(jinja2.runtime.Undefined):
def __str__(self):
return "{{ %s }}" % self._undefined_name
def __unicode__(self):
return "{{ %s }}" % self._undefined_name
def template_from_string(basedir, data, vars):
''' run a file through the (Jinja2) templating engine '''
try:
if type(data) == str:
data = unicode(data, 'utf-8')
environment = jinja2.Environment(trim_blocks=True, undefined=J2Undefined)
environment = jinja2.Environment(trim_blocks=True) # undefined=J2Undefined)
environment.filters.update(_get_filter_plugins())
# TODO: may need some way of using lookup plugins here seeing we aren't calling
# the legacy engine, lookup() as a function, perhaps?
@ -421,4 +417,6 @@ def template_from_string(basedir, data, vars):
t = environment.from_string(data)
res = jinja2.utils.concat(t.root_render_func(t.new_context(_jinja2_vars(basedir, vars, t.globals), shared=True)))
return res
except jinja2.exceptions.UndefinedError:
return data