From 5f4bf813b148069eb6af1621e06d94f6817d64b0 Mon Sep 17 00:00:00 2001 From: Petros Moisiadis Date: Wed, 1 Aug 2012 19:13:07 +0300 Subject: [PATCH 1/2] added an 'ignore_errors' option to tasks Failed tasks with ignore_errors=True, allow the remaining tasks in the play to be executed as if no failure happened. A failure notice is still written to the output as well as an '...ignoring' message. --- lib/ansible/callbacks.py | 4 +++- lib/ansible/playbook/task.py | 10 +++++++--- lib/ansible/runner/__init__.py | 8 +++++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/ansible/callbacks.py b/lib/ansible/callbacks.py index ce06af6609..2a49316122 100644 --- a/lib/ansible/callbacks.py +++ b/lib/ansible/callbacks.py @@ -259,7 +259,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks): else: print "fatal: [%s] => %s" % (host, msg) - def on_failed(self, host, results): + def on_failed(self, host, results, ignore_errors): item = results.get('item', None) @@ -269,6 +269,8 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks): msg = "failed: [%s] => %s" % (host, utils.jsonify(results)) print stringc(msg, 'red') + if ignore_errors: + print stringc("...ignoring", 'yellow') def on_ok(self, host, host_result): diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index c1ff016c68..1cdf450cde 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -23,13 +23,13 @@ class Task(object): __slots__ = [ 'name', 'action', 'only_if', 'async_seconds', 'async_poll_interval', 'notify', 'module_name', 'module_args', 'module_vars', - 'play', 'notified_by', 'tags', 'with_items', 'first_available_file' + 'play', 'notified_by', 'tags', 'with_items', 'first_available_file', 'ignore_errors' ] # to prevent typos and such VALID_KEYS = [ 'name', 'action', 'only_if', 'async', 'poll', 'notify', 'with_items', 'first_available_file', - 'include', 'tags' + 'include', 'tags', 'ignore_errors' ] def __init__(self, play, ds, module_vars=None): @@ -62,7 +62,8 @@ class Task(object): self.notify = ds.get('notify', []) self.first_available_file = ds.get('first_available_file', None) self.with_items = ds.get('with_items', None) - + self.ignore_errors = ds.get('ignore_errors', False) + # notify can be a string or a list, store as a list if isinstance(self.notify, basestring): self.notify = [ self.notify ] @@ -97,6 +98,9 @@ class Task(object): self.with_items = [ ] self.module_vars['items'] = self.with_items + # make ignore_errors accessable to Runner code + self.module_vars['ignore_errors'] = self.ignore_errors + # tags allow certain parts of a playbook to be run without running the whole playbook apply_tags = ds.get('tags', None) if apply_tags is not None: diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 4468f107c1..351bbf977a 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -602,7 +602,13 @@ class Runner(object): if 'skipped' in data: self.callbacks.on_skipped(result.host) elif not result.is_successful(): - self.callbacks.on_failed(result.host, data) + ignore_errors = self.module_vars['ignore_errors'] + self.callbacks.on_failed(result.host, data, ignore_errors) + if ignore_errors: + if 'failed' in result.result: + result.result['failed'] = False + if 'rc' in result.result: + result.result['rc'] = 0 else: self.callbacks.on_ok(result.host, data) return result From 2ac4acbf9760a1bdc80a2ad7c15e4b9128168ce6 Mon Sep 17 00:00:00 2001 From: Petros Moisiadis Date: Wed, 1 Aug 2012 22:17:16 +0300 Subject: [PATCH 2/2] fixes for tests --- lib/ansible/callbacks.py | 6 +++--- lib/ansible/runner/__init__.py | 2 +- test/TestPlayBook.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/ansible/callbacks.py b/lib/ansible/callbacks.py index 2a49316122..f32a56ebbb 100644 --- a/lib/ansible/callbacks.py +++ b/lib/ansible/callbacks.py @@ -147,7 +147,7 @@ class DefaultRunnerCallbacks(object): def __init__(self): pass - def on_failed(self, host, res): + def on_failed(self, host, res, ignore_errors=False): pass def on_ok(self, host, res): @@ -185,7 +185,7 @@ class CliRunnerCallbacks(DefaultRunnerCallbacks): self.options = None self._async_notified = {} - def on_failed(self, host, res): + def on_failed(self, host, res, ignore_errors=False): self._on_any(host,res) @@ -259,7 +259,7 @@ class PlaybookRunnerCallbacks(DefaultRunnerCallbacks): else: print "fatal: [%s] => %s" % (host, msg) - def on_failed(self, host, results, ignore_errors): + def on_failed(self, host, results, ignore_errors=False): item = results.get('item', None) diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 351bbf977a..9ab2fc6edb 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -602,7 +602,7 @@ class Runner(object): if 'skipped' in data: self.callbacks.on_skipped(result.host) elif not result.is_successful(): - ignore_errors = self.module_vars['ignore_errors'] + ignore_errors = self.module_vars.get('ignore_errors', False) self.callbacks.on_failed(result.host, data, ignore_errors) if ignore_errors: if 'failed' in result.result: diff --git a/test/TestPlayBook.py b/test/TestPlayBook.py index 8e5704e7ee..cf13ee3852 100644 --- a/test/TestPlayBook.py +++ b/test/TestPlayBook.py @@ -54,8 +54,8 @@ class TestCallbacks(object): def on_unreachable(self, host, msg): EVENTS.append([ 'unreachable', [ host, msg ]]) - def on_failed(self, host, results): - EVENTS.append([ 'failed', [ host, results ]]) + def on_failed(self, host, results, ignore_errors): + EVENTS.append([ 'failed', [ host, results, ignore_errors ]]) def on_ok(self, host, result): # delete certain info from host_result to make test comparisons easier