Add failed_when module variable.

Implementation note: Ternery operator trick for python prior to 2.5 is used.
(test and [when_true_value] or [when_false_value])[0]
http://stackoverflow.com/questions/394809/ternary-conditional-operator-in-python#comment1466794_394887
pull/4073/head
Hiroaki Nakamura 11 years ago
parent 808d9596b2
commit 2357194b39

@ -189,7 +189,7 @@ class AggregateStats(object):
for (host, value) in runner_results.get('contacted', {}).iteritems(): for (host, value) in runner_results.get('contacted', {}).iteritems():
if not ignore_errors and (('failed' in value and bool(value['failed'])) or if not ignore_errors and (('failed' in value and bool(value['failed'])) or
('rc' in value and value['rc'] != 0)): ('failed_when_result' in value and [value['failed_when_result']] or ['rc' in value and value['rc'] != 0])[0]):
self._increment('failures', host) self._increment('failures', host)
elif 'skipped' in value and bool(value['skipped']): elif 'skipped' in value and bool(value['skipped']):
self._increment('skipped', host) self._increment('skipped', host)

@ -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', 'changed_when', 'always_run' 'any_errors_fatal', 'changed_when', 'failed_when', 'always_run'
] ]
# 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', 'changed_when', 'always_run' 'any_errors_fatal', 'changed_when', 'failed_when', 'always_run'
] ]
def __init__(self, play, ds, module_vars=None, default_vars=None, additional_conditions=None): def __init__(self, play, ds, module_vars=None, default_vars=None, additional_conditions=None):
@ -88,7 +88,7 @@ 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 in [ 'changed_when', 'when']: elif x in [ 'changed_when', 'failed_when', 'when']:
ds[x] = "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:
@ -167,6 +167,11 @@ class Task(object):
if self.changed_when is not None: if self.changed_when is not None:
self.changed_when = utils.compile_when_to_only_if(self.changed_when) self.changed_when = utils.compile_when_to_only_if(self.changed_when)
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', [])
@ -223,6 +228,7 @@ class Task(object):
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['register'] = self.register
self.module_vars['changed_when'] = self.changed_when self.module_vars['changed_when'] = self.changed_when
self.module_vars['failed_when'] = self.failed_when
self.module_vars['always_run'] = self.always_run self.module_vars['always_run'] = self.always_run
# 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

@ -507,7 +507,7 @@ class Runner(object):
for x in results: for x in results:
if x.get('changed') == True: if x.get('changed') == True:
all_changed = True all_changed = True
if (x.get('failed') == True) or (('rc' in x) and (x['rc'] != 0)): if (x.get('failed') == True) or ('failed_when_result' in x and [x['failed_when_result']] or [('rc' in x) and (x['rc'] != 0)])[0]:
all_failed = True all_failed = True
break break
msg = 'All items completed' msg = 'All items completed'
@ -669,13 +669,17 @@ class Runner(object):
) )
changed_when = self.module_vars.get('changed_when') changed_when = self.module_vars.get('changed_when')
if changed_when is not None: failed_when = self.module_vars.get('failed_when')
if changed_when is not None or failed_when is not None:
register = self.module_vars.get('register') register = self.module_vars.get('register')
if register is not None: if register is not None:
if 'stdout' in data: if 'stdout' in data:
data['stdout_lines'] = data['stdout'].splitlines() data['stdout_lines'] = data['stdout'].splitlines()
inject[register] = data inject[register] = data
data['changed'] = utils.check_conditional(changed_when, self.basedir, inject, fail_on_undefined=self.error_on_undefined_vars) if changed_when is not None:
data['changed'] = utils.check_conditional(changed_when, self.basedir, inject, fail_on_undefined=self.error_on_undefined_vars)
if failed_when is not None:
data['failed_when_result'] = data['failed'] = utils.check_conditional(failed_when, self.basedir, inject, fail_on_undefined=self.error_on_undefined_vars)
if is_chained: if is_chained:
# no callbacks # no callbacks

@ -59,5 +59,5 @@ class ReturnData(object):
return self.comm_ok return self.comm_ok
def is_successful(self): def is_successful(self):
return self.comm_ok and (self.result.get('failed', False) == False) and (self.result.get('rc',0) == 0) return self.comm_ok and (self.result.get('failed', False) == False) and ('failed_when_result' in self.result and [not self.result['failed_when_result']] or [self.result.get('rc',0) == 0])[0]

@ -474,6 +474,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_failed_when(self):
test_callbacks = TestCallbacks()
playbook = ansible.playbook.PlayBook(
playbook=os.path.join(self.test_dir, 'playbook-failed_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": 2,
"failures": 1,
"ok": 2,
"skipped": 0,
"unreachable": 0
}
}
print "**EXPECTED**"
print utils.jsonify(expected, format=True)
assert utils.jsonify(expected, format=True) == utils.jsonify(actual,format=True)
def test_playbook_always_run(self): def test_playbook_always_run(self):
test_callbacks = TestCallbacks() test_callbacks = TestCallbacks()

@ -0,0 +1,15 @@
---
- hosts: all
connection: local
gather_facts: False
tasks:
- action: shell exit 0
register: exit
failed_when: not exit.rc in [0, 1]
- action: shell exit 1
register: exit
failed_when: exit.rc not in [0, 1]
- action: shell exit 2
register: exit
failed_when: exit.rc not in [0, 1]
Loading…
Cancel
Save