Merge branch 'changed_when' into devel

This commit is contained in:
Michael DeHaan 2013-07-21 10:37:56 -04:00
commit f3c69040b4
7 changed files with 79 additions and 10 deletions

View file

@ -360,7 +360,7 @@ class PlayBook(object):
# extra vars need to always trump - so update again following the facts # extra vars need to always trump - so update again following the facts
self.SETUP_CACHE[host].update(self.extra_vars) self.SETUP_CACHE[host].update(self.extra_vars)
if task.register: if task.register:
if 'stdout' in result: if 'stdout' in result and 'stdout_lines' not in result:
result['stdout_lines'] = result['stdout'].splitlines() result['stdout_lines'] = result['stdout'].splitlines()
self.SETUP_CACHE[host][task.register] = result self.SETUP_CACHE[host][task.register] = result
@ -368,7 +368,7 @@ class PlayBook(object):
if task.ignore_errors and task.register: if task.ignore_errors and task.register:
failed = results.get('failed', {}) failed = results.get('failed', {})
for host, result in failed.iteritems(): for host, result in failed.iteritems():
if 'stdout' in result: if 'stdout' in result and 'stdout_lines' not in result:
result['stdout_lines'] = result['stdout'].splitlines() result['stdout_lines'] = result['stdout'].splitlines()
self.SETUP_CACHE[host][task.register] = result self.SETUP_CACHE[host][task.register] = result

View file

@ -29,7 +29,7 @@ class Task(object):
'delegate_to', 'first_available_file', 'ignore_errors', 'delegate_to', 'first_available_file', 'ignore_errors',
'local_action', 'transport', 'sudo', 'sudo_user', 'sudo_pass', 'local_action', 'transport', 'sudo', 'sudo_user', 'sudo_pass',
'items_lookup_plugin', 'items_lookup_terms', 'environment', 'args', 'items_lookup_plugin', 'items_lookup_terms', 'environment', 'args',
'any_errors_fatal' 'any_errors_fatal', 'changed_when'
] ]
# to prevent typos and such # to prevent typos and such
@ -38,7 +38,7 @@ class Task(object):
'first_available_file', 'include', 'tags', 'register', 'ignore_errors', 'first_available_file', 'include', 'tags', 'register', 'ignore_errors',
'delegate_to', 'local_action', 'transport', 'sudo', 'sudo_user', 'delegate_to', 'local_action', 'transport', 'sudo', 'sudo_user',
'sudo_pass', 'when', 'connection', 'environment', 'args', 'sudo_pass', 'when', 'connection', 'environment', 'args',
'any_errors_fatal' 'any_errors_fatal', 'changed_when'
] ]
def __init__(self, play, ds, module_vars=None, additional_conditions=None): def __init__(self, play, ds, module_vars=None, additional_conditions=None):
@ -88,8 +88,8 @@ class Task(object):
else: else:
raise errors.AnsibleError("cannot find lookup plugin named %s for usage in with_%s" % (plugin_name, plugin_name)) raise errors.AnsibleError("cannot find lookup plugin named %s for usage in with_%s" % (plugin_name, plugin_name))
elif x == 'when': elif x in [ 'changed_when', 'when']:
ds['when'] = "jinja2_compare %s" % (ds[x]) ds[x] = "jinja2_compare %s" % (ds[x])
elif x.startswith("when_"): elif x.startswith("when_"):
if 'when' in ds: if 'when' in ds:
raise errors.AnsibleError("multiple when_* statements specified in task %s" % (ds.get('name', ds['action']))) raise errors.AnsibleError("multiple when_* statements specified in task %s" % (ds.get('name', ds['action'])))
@ -161,6 +161,10 @@ class Task(object):
# load various attributes # load various attributes
self.only_if = ds.get('only_if', 'True') self.only_if = ds.get('only_if', 'True')
self.when = ds.get('when', None) self.when = ds.get('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.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
@ -214,6 +218,8 @@ class Task(object):
# make ignore_errors accessable to Runner code # make ignore_errors accessable to Runner code
self.module_vars['ignore_errors'] = self.ignore_errors self.module_vars['ignore_errors'] = self.ignore_errors
self.module_vars['register'] = self.register
self.module_vars['changed_when'] = self.changed_when
# tags allow certain parts of a playbook to be run without running the whole playbook # tags allow certain parts of a playbook to be run without running the whole playbook
apply_tags = ds.get('tags', None) apply_tags = ds.get('tags', None)

View file

@ -528,7 +528,8 @@ class Runner(object):
self.conditional = [ self.conditional ] self.conditional = [ self.conditional ]
for cond in self.conditional: for cond in self.conditional:
if not utils.check_conditional(cond, self.basedir, inject):
if not utils.check_conditional(cond, self.basedir, inject, fail_on_undefined=self.error_on_undefined_vars):
result = utils.jsonify(dict(changed=False, skipped=True)) result = utils.jsonify(dict(changed=False, skipped=True))
self.callbacks.on_skipped(host, inject.get('item',None)) self.callbacks.on_skipped(host, inject.get('item',None))
return ReturnData(host=host, result=result) return ReturnData(host=host, result=result)
@ -629,6 +630,15 @@ class Runner(object):
module_name=module_name module_name=module_name
) )
changed_when = self.module_vars.get('changed_when')
if changed_when is not None:
register = self.module_vars.get('register')
if register is not None:
if 'stdout' in data:
data['stdout_lines'] = data['stdout'].splitlines()
inject[register] = data
data['changed'] = utils.check_conditional(changed_when, self.basedir, inject, fail_on_undefined=self.error_on_undefined_vars)
if is_chained: if is_chained:
# no callbacks # no callbacks
return result return result

View file

@ -58,7 +58,7 @@ class ActionModule(object):
data = {} data = {}
data.update(inject) data.update(inject)
data.update(inject['hostvars'][host]) data.update(inject['hostvars'][host])
if not check_conditional(self.runner.basedir, self.runner.conditional, data): if not check_conditional(self.runner.basedir, self.runner.conditional, data, fail_on_undefined=self.runner.error_on_undefined_vars):
continue continue
group_name = template.template(self.runner.basedir, args['key'], data) group_name = template.template(self.runner.basedir, args['key'], data)
group_name = group_name.replace(' ','-') group_name = group_name.replace(' ','-')

View file

@ -155,14 +155,14 @@ 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): def check_conditional(conditional, basedir, inject, fail_on_undefined=False):
if conditional.startswith("jinja2_compare"): if conditional.startswith("jinja2_compare"):
conditional = conditional.replace("jinja2_compare ","") conditional = conditional.replace("jinja2_compare ","")
# allow variable names # allow variable names
if conditional in inject: if conditional in inject:
conditional = inject[conditional] conditional = inject[conditional]
conditional = template.template(basedir, conditional, inject) conditional = template.template(basedir, conditional, inject, fail_on_undefined=fail_on_undefined)
# a Jinja2 evaluation that results in something Python can eval! # a Jinja2 evaluation that results in something Python can eval!
presented = "{% if " + conditional + " %} True {% else %} False {% endif %}" presented = "{% if " + conditional + " %} True {% else %} False {% endif %}"
conditional = template.template(basedir, presented, inject) conditional = template.template(basedir, presented, inject)

View file

@ -407,6 +407,34 @@ class TestPlaybook(unittest.TestCase):
assert utils.jsonify(expected, format=True) == utils.jsonify(actual,format=True) assert utils.jsonify(expected, format=True) == utils.jsonify(actual,format=True)
def test_playbook_changed_when(self):
test_callbacks = TestCallbacks()
playbook = ansible.playbook.PlayBook(
playbook=os.path.join(self.test_dir, 'playbook-changed_when.yml'),
host_list='test/ansible_hosts',
stats=ans_callbacks.AggregateStats(),
callbacks=test_callbacks,
runner_callbacks=test_callbacks
)
actual = playbook.run()
# if different, this will output to screen
print "**ACTUAL**"
print utils.jsonify(actual, format=True)
expected = {
"localhost": {
"changed": 3,
"failures": 0,
"ok": 6,
"skipped": 0,
"unreachable": 0
}
}
print "**EXPECTED**"
print utils.jsonify(expected, format=True)
assert utils.jsonify(expected, format=True) == utils.jsonify(actual,format=True)
def _compare_file_output(self, filename, expected_lines): def _compare_file_output(self, filename, expected_lines):
actual_lines = [] actual_lines = []
with open(filename) as f: with open(filename) as f:

View file

@ -0,0 +1,25 @@
---
- hosts: all
connection: local
gather_facts: False
tasks:
- action: command echo first action
- action: command echo second action
register: var
changed_when: "'X' in var.stdout"
- action: shell exit 2
register: exit
ignore_errors: yes
changed_when: "exit.rc < 1"
- action: command echo third action
changed_when: false
- action: file path=/ state=directory
changed_when: true
- action: command echo {{item}}
register: out
changed_when: "'e' in out.stdout"
with_items:
- hello
- foo
- bye