From 326bb24a89e0e1d14971b235724b55fe94e231aa Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Tue, 15 Mar 2016 15:39:44 +0100 Subject: [PATCH] Various improvements, now supports different verbosity levels --- lib/ansible/plugins/callback/dense.py | 210 +++++++++++++++++++------- 1 file changed, 153 insertions(+), 57 deletions(-) diff --git a/lib/ansible/plugins/callback/dense.py b/lib/ansible/plugins/callback/dense.py index a2a7b6e96d7..10b3db30be3 100644 --- a/lib/ansible/plugins/callback/dense.py +++ b/lib/ansible/plugins/callback/dense.py @@ -21,9 +21,13 @@ __metaclass__ = type from ansible.plugins.callback.default import CallbackModule as CallbackModule_default -import sys -import time +try: + from __main__ import display +except ImportError: + from ansible.utils.display import Display + display = Display() +import sys # Design goals: # @@ -43,7 +47,7 @@ import time # - We use the cursor to indicate where in the task we are. # Output after the prompt is the output of the previous task # -# - Use the same color-conventions of Ansible +# + Use the same color-conventions of Ansible # TODO: @@ -51,8 +55,8 @@ import time # + Ensure all other output is properly displayed # + Properly test for terminal capabilities, and fall back to default # + Modify Ansible mechanism so we don't need to use sys.stdout directly -# + Check verbosity 1, display task details on change/failure ! -# + Check verbosity 2, use default callback for everything +# + Remove items from result to compact the json output when using -v +# + Make colored output nicer # Taken from Dstat @@ -117,27 +121,64 @@ class CallbackModule(CallbackModule_default): CALLBACK_TYPE = 'stdout' CALLBACK_NAME = 'dense' - hosts = [] - keep = False -# task = '' - tasknr = 0 - def __init__(self): + + # From CallbackModule + if display: + self._display = display + else: + self._display = global_display + + if self._display.verbosity >= 4: + name = getattr(self, 'CALLBACK_NAME', 'unnamed') + ctype = getattr(self, 'CALLBACK_TYPE', 'old') + version = getattr(self, 'CALLBACK_VERSION', '1.0') + self._display.vvvv('Loaded callback %s of type %s, v%s' % (name, ctype, version)) + + self.super_ref = super(CallbackModule, self) + self.super_ref.__init__() + + if self._display.verbosity > 1: + return + self.hosts = [] + self.keep = False + self.shown_title = False + self.tasknr = 0 + self.playnr = 0 sys.stdout.write(ansi.save + ansi.clearline) sys.stdout.flush() - def _add_host(self, name, status): + def _add_host(self, result, status): + self.hosts.append((result._host.get_name(), status)) + self._display_progress() # Ensure that tasks with changes/failures stay on-screen - if not self.keep and status in ['changed', 'failed', 'unreachable']: + if status in ['changed', 'failed', 'unreachable']: self.keep = True - self.hosts.append((name, status)) - self._print_status() + if self._display.verbosity == 1: + self._display_task_banner() + # TODO: clean up result output, eg. remove changed, delta, end, start, ... + if status == 'changed': + self.super_ref.v2_runner_on_ok(result) + elif status == 'failed': + self.super_ref.v2_runner_on_failed(result) + elif status == 'unreachable': + self.super_ref.v2_runner_on_unreachable(result) + + def _display_task_banner(self): + if not self.shown_title: + self.shown_title = True + sys.stdout.write(ansi.restore + ansi.clearline) + sys.stdout.write(ansi.underline + 'task %d: %s' % (self.tasknr, self.task.get_name().strip())) + sys.stdout.write(ansi.restore + '\n' + ansi.reset + ansi.clearline) + sys.stdout.flush() + else: + sys.stdout.write(ansi.restore + ansi.clearline) - def _print_status(self): + def _display_progress(self): # Always rewrite the complete line - sys.stdout.write(ansi.restore) - sys.stdout.write(ansi.clearline) - sys.stdout.write('- task %d: ' % self.tasknr) + sys.stdout.write(ansi.restore + ansi.clearline + ansi.underline) + sys.stdout.write('task %d:' % self.tasknr) + sys.stdout.write(ansi.reset + ' ') sys.stdout.flush() # Print out each host with its own status-color @@ -145,7 +186,7 @@ class CallbackModule(CallbackModule_default): if status == 'ok': color = ansi.darkgreen elif status == 'changed': - color = ansi.yellow + color = ansi.darkyellow elif status == 'skipped': color = ansi.darkcyan elif status == 'failed': @@ -160,62 +201,117 @@ class CallbackModule(CallbackModule_default): # Place cursor at start of the line sys.stdout.write(ansi.default) + def v2_playbook_on_play_start(self, play): + if self._display.verbosity > 1: + self.super_ref.v2_playbook_on_play_start(play) + return + + self.tasknr = 0 + self.playnr += 1 + self.play = play + # Leave the previous task on screen (as it has changes/errors) + if self.keep: + sys.stdout.write(ansi.restore + '\n' + ansi.save + ansi.clearline + ansi.bold) + else: + sys.stdout.write(ansi.restore + ansi.clearline + ansi.bold) + name = play.get_name().strip() + if name: + sys.stdout.write('PLAY %d: %s' % (self.playnr, name.upper())) + else: + sys.stdout.write('PLAY %d' % self.playnr) + # Always leave the PLAY output on screen + sys.stdout.write(ansi.restore + '\n' + ansi.save + ansi.reset + ansi.clearline) + sys.stdout.flush() + + def v2_playbook_on_task_start(self, task, is_conditional): + if self._display.verbosity > 1: + self.super_ref.v2_playbook_on_task_start(task, is_conditional) + return + + # Leave the previous task on screen (as it has changes/errors) + if self._display.verbosity == 0 and self.keep: + sys.stdout.write(ansi.restore + '\n' + ansi.save + ansi.clearline) + + # Reset counters at the start of each new task + self.keep = False + self.shown_title = False + self.hosts = [] + self.task = task + + # Enumerate task if not setup (task names are too long for dense output) + if task.get_name() != 'setup': + self.tasknr += 1 +# self.task = task.get_name().strip() + # Write the next task on screen (behind the prompt is the previous output) + sys.stdout.write(ansi.restore + ansi.underline) + sys.stdout.write('task %d' % self.tasknr) + sys.stdout.write(ansi.reset) + sys.stdout.flush() + def v2_runner_on_failed(self, result, ignore_errors=False): - self._add_host(result._host.get_name(), 'failed') + if self._display.verbosity > 1: + self.super_ref.v2_runner_on_failed(result, ignore_errors) + return + + self._add_host(result, 'failed') def v2_runner_on_ok(self, result): + if self._display.verbosity > 1: + self.super_ref.v2_runner_on_ok(result) + return + if result._result.get('changed', False): - self._add_host(result._host.get_name(), 'changed') + self._add_host(result, 'changed') else: - self._add_host(result._host.get_name(), 'ok') + self._add_host(result, 'ok') def v2_runner_on_skipped(self, result): - self._add_host(result._host.get_name(), 'skipped') + if self._display.verbosity > 1: + self.super_ref.v2_runner_on_skipped(result) + return + + self._add_host(result, 'skipped') def v2_runner_on_unreachable(self, result): - self._add_host(result._host.get_name(), 'unreachable') + if self._display.verbosity > 1: + self.super_ref.v2_runner_on_unreachable(result) + return + + self._add_host(result, 'unreachable') + + def v2_runner_on_include(self, included_file): + if self._display.verbosity > 1: + self.super_ref.v2_runner_on_include(included_file) + + def v2_playbook_item_on_ok(self, result): + if self._display.verbosity > 1: + self.super_ref.v2_playbook_item_on_ok(result) + + # TBD + + def v2_playbook_item_on_failed(self, result): + if self._display.verbosity > 1: + self.super_ref.v2_playbook_item_on_failed(result) + + # TBD def v2_playbook_item_on_skipped(self, result): - pass + if self._display.verbosity > 1: + self.super_ref.v2_playbook_item_on_skipped(result) + + # TBD def v2_playbook_on_no_hosts_remaining(self): + if self._display.verbosity > 1: + self.super_ref.v2_runner_on_no_hosts_remaining() + return + # TBD if self.keep: - sys.stdout.write(ansi.restore + '\n' + ansi.save) + sys.stdout.write(ansi.restore + '\n' + ansi.save + ansi.clearline) else: sys.stdout.write(ansi.restore + ansi.clearline) sys.stdout.write(ansi.white + ansi.redbg + 'NO MORE HOSTS LEFT' + ansi.default) sys.stdout.write(ansi.reset) - sys.stdout.flush() - - def v2_playbook_on_task_start(self, task, is_conditional): - # Leave the previous task on screen (as it has changes/errors) - if self.keep: - sys.stdout.write(ansi.restore + '\n' + ansi.save + ansi.clearline) - # Reset counters at the start of each new task - self.keep = False - self.hosts = [] - # Enumerate task (task names are too long for dense output) - self.tasknr += 1 -# self.task = task.get_name().strip() - # Write the next task on screen (behind the prompt is the previous output) - sys.stdout.write(ansi.restore) - sys.stdout.write('- task %d|' % self.tasknr) - sys.stdout.flush() - - def v2_playbook_on_play_start(self, play): - self.tasknr = 0 - # Leave the previous task on screen (as it has changes/errors) - if self.keep: - sys.stdout.write(ansi.restore + '\n' + ansi.save + ansi.clearline) - else: - sys.stdout.write(ansi.restore + ansi.clearline) - name = play.get_name().strip() - if name: - sys.stdout.write('PLAY [%s]' % name) - else: - sys.stdout.write('PLAY') - # Always leave the PLAY output on screen - sys.stdout.write(ansi.restore + '\n' + ansi.save + ansi.clearline) sys.stdout.flush() \ No newline at end of file