Coverage for lib/ansible/plugins/callback/dense.py : 28%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# (c) 2016, Dag Wieers <dag@wieers.com> # (c) 2017 Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
callback: dense type: stdout short_description: minimal stdout output extends_documentation_fragment: - default_callback description: - When in verbose mode it will act the same as the default callback version_added: "2.3" requirements: - set as stdout in configuation '''
except ImportError: pass
except ImportError: from ansible.utils.display import Display display = Display()
# Design goals: # # + On screen there should only be relevant stuff # - How far are we ? (during run, last line) # - What issues occurred # - What changes occurred # - Diff output (in diff-mode) # # + If verbosity increases, act as default output # So that users can easily switch to default for troubleshooting # # + Rewrite the output during processing # - We use the cursor to indicate where in the task we are. # Output after the prompt is the output of the previous task. # - If we would clear the line at the start of a task, there would often # be no information at all, so we leave it until it gets updated # # + Use the same color-conventions of Ansible # # + Ensure the verbose output (-v) is also dense. # Remove information that is not essential (eg. timestamps, status)
# TODO: # # + Properly test for terminal capabilities, and fall back to default # + Modify Ansible mechanism so we don't need to use sys.stdout directly # + Find an elegant solution for progress bar line wrapping
# FIXME: Importing constants as C simply does not work, beats me :-/ # from ansible import constants as C
# Taken from Dstat
# clearline = '\033[K'
ok=vt100.darkgreen, changed=vt100.darkyellow, skipped=vt100.darkcyan, ignored=vt100.cyanbg + vt100.red, failed=vt100.darkred, unreachable=vt100.red, )
''' This is the dense callback interface, where screen estate is still valued. '''
# From CallbackModule self._display = display
if HAS_OD:
self.disabled = False self.super_ref = super(CallbackModule, self) self.super_ref.__init__()
# Attributes to remove from results for more density self.removed_attributes = ( # 'changed', 'delta', # 'diff', 'end', 'failed', 'failed_when_result', 'invocation', 'start', 'stdout_lines', )
# Initiate data structures self.hosts = OrderedDict() self.keep = False self.shown_title = False self.count = dict(play=0, handler=0, task=0) self.type = 'foo'
# Start immediately on the first line sys.stdout.write(vt100.reset + vt100.save + vt100.clearline) sys.stdout.flush() else: display.warning("The 'dense' callback plugin requires OrderedDict which is not available in this version of python, disabling.") self.disabled = True
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline)
name = result._host.get_name()
# Add a new status in case a failed task is ignored if status == 'failed' and result._task.ignore_errors: status = 'ignored'
# Check if we have to update an existing state (when looping over items) if name not in self.hosts: self.hosts[name] = dict(state=status) elif states.index(self.hosts[name]['state']) < states.index(status): self.hosts[name]['state'] = status
# Store delegated hostname, if needed delegated_vars = result._result.get('_ansible_delegated_vars', None) if delegated_vars: self.hosts[name]['delegate'] = delegated_vars['ansible_host']
# Print progress bar self._display_progress(result)
# # Ensure that tasks with changes/failures stay on-screen, and during diff-mode # if status in ['changed', 'failed', 'unreachable'] or (result.get('_diff_mode', False) and result._resultget('diff', False)): # Ensure that tasks with changes/failures stay on-screen if status in ['changed', 'failed', 'unreachable']: self.keep = True
if self._display.verbosity == 1: # Print task title, if needed self._display_task_banner() self._display_results(result, status)
# Remove non-essential atributes for attr in self.removed_attributes: if attr in result: del(result[attr])
# Remove empty attributes (list, dict, str) for attr in result.copy(): if isinstance(result[attr], (MutableSequence, MutableMapping, binary_type, text_type)): if not result[attr]: del(result[attr])
if 'exception' in result: # Remove the exception from the result so it's not shown every time del result['exception']
if self._display.verbosity == 1: return "An exception occurred during task execution. To see the full traceback, use -vvv."
# Always rewrite the complete line sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline + vt100.nolinewrap + vt100.underline) sys.stdout.write('%s %d:' % (self.type, self.count[self.type])) sys.stdout.write(vt100.reset) sys.stdout.flush()
# Print out each host in its own status-color for name in self.hosts: sys.stdout.write(' ') if self.hosts[name].get('delegate', None): sys.stdout.write(self.hosts[name]['delegate'] + '>') sys.stdout.write(colors[self.hosts[name]['state']] + name + vt100.reset) sys.stdout.flush()
# if result._result.get('diff', False): # sys.stdout.write('\n' + vt100.linewrap) sys.stdout.write(vt100.linewrap)
# self.keep = True
if not self.shown_title: self.shown_title = True sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline + vt100.underline) sys.stdout.write('%s %d: %s' % (self.type, self.count[self.type], self.task.get_name().strip())) sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline) sys.stdout.flush() else: sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline) self.keep = False
# Leave the previous task on screen (as it has changes/errors) if self._display.verbosity == 0 and self.keep: sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline) else: sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline) self.keep = False
self._clean_results(result._result)
dump = '' if result._task.action == 'include': return elif status == 'ok': return elif status == 'ignored': dump = self._handle_exceptions(result._result) elif status == 'failed': dump = self._handle_exceptions(result._result) elif status == 'unreachable': dump = result._result['msg']
if not dump: dump = self._dump_results(result._result)
if result._task.loop and 'results' in result._result: self._process_items(result) else: sys.stdout.write(colors[status] + status + ': ')
delegated_vars = result._result.get('_ansible_delegated_vars', None) if delegated_vars: sys.stdout.write(vt100.reset + result._host.get_name() + '>' + colors[status] + delegated_vars['ansible_host']) else: sys.stdout.write(result._host.get_name())
sys.stdout.write(': ' + dump + '\n') sys.stdout.write(vt100.reset + vt100.save + vt100.clearline) sys.stdout.flush()
if status == 'changed': self._handle_warnings(result._result)
# Leave the previous task on screen (as it has changes/errors) if self._display.verbosity == 0 and self.keep: sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline + vt100.bold) else: sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline + vt100.bold)
# Reset at the start of each play self.keep = False self.count.update(dict(handler=0, task=0)) self.count['play'] += 1 self.play = play
# Write the next play on screen IN UPPERCASE, and make it permanent name = play.get_name().strip() if not name: name = 'unnamed' sys.stdout.write('PLAY %d: %s' % (self.count['play'], name.upper())) sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline) sys.stdout.flush()
# Leave the previous task on screen (as it has changes/errors) if self._display.verbosity == 0 and self.keep: sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline + vt100.underline) else: # Do not clear line, since we want to retain the previous output sys.stdout.write(vt100.restore + vt100.reset + vt100.underline)
# Reset at the start of each task self.keep = False self.shown_title = False self.hosts = OrderedDict() self.task = task self.type = 'task'
# Enumerate task if not setup (task names are too long for dense output) if task.get_name() != 'setup': self.count['task'] += 1
# Write the next task on screen (behind the prompt is the previous output) sys.stdout.write('%s %d.' % (self.type, self.count[self.type])) sys.stdout.write(vt100.reset) sys.stdout.flush()
# Leave the previous task on screen (as it has changes/errors) if self._display.verbosity == 0 and self.keep: sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline + vt100.underline) else: sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline + vt100.underline)
# Reset at the start of each handler self.keep = False self.shown_title = False self.hosts = OrderedDict() self.task = task self.type = 'handler'
# Enumerate handler if not setup (handler names may be too long for dense output) if task.get_name() != 'setup': self.count[self.type] += 1
# Write the next task on screen (behind the prompt is the previous output) sys.stdout.write('%s %d.' % (self.type, self.count[self.type])) sys.stdout.write(vt100.reset) sys.stdout.flush()
# TBD sys.stdout.write('cleanup.') sys.stdout.flush()
self._add_host(result, 'failed')
if result._result.get('changed', False): self._add_host(result, 'changed') else: self._add_host(result, 'ok')
self._add_host(result, 'skipped')
self._add_host(result, 'unreachable')
pass
sys.stdout.write(vt100.bold) self.super_ref.v2_runner_on_file_diff(result, diff) sys.stdout.write(vt100.reset)
sys.stdout.write(vt100.bold) self.super_ref.v2_on_file_diff(result) sys.stdout.write(vt100.reset)
# Old definition in v2.0 self.v2_runner_item_on_ok(result)
if result._result.get('changed', False): self._add_host(result, 'changed') else: self._add_host(result, 'ok')
# Old definition in v2.0 self.v2_runner_item_on_failed(result)
self._add_host(result, 'failed')
# Old definition in v2.0 self.v2_runner_item_on_skipped(result)
self._add_host(result, 'skipped')
if self._display.verbosity == 0 and self.keep: sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline) else: sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline) self.keep = False
sys.stdout.write(vt100.white + vt100.redbg + 'NO MORE HOSTS LEFT') sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline) sys.stdout.flush()
pass
if self._display.verbosity == 0 and self.keep: sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline) else: sys.stdout.write(vt100.restore + vt100.reset + vt100.clearline)
# In normal mode screen output should be sufficient, summary is redundant if self._display.verbosity == 0: return
sys.stdout.write(vt100.bold + vt100.underline) sys.stdout.write('SUMMARY')
sys.stdout.write(vt100.restore + vt100.reset + '\n' + vt100.save + vt100.clearline) sys.stdout.flush()
hosts = sorted(stats.processed.keys()) for h in hosts: t = stats.summarize(h) self._display.display(u"%s : %s %s %s %s" % ( hostcolor(h, t), colorize(u'ok', t['ok'], C.COLOR_OK), colorize(u'changed', t['changed'], C.COLOR_CHANGED), colorize(u'unreachable', t['unreachable'], C.COLOR_UNREACHABLE), colorize(u'failed', t['failures'], C.COLOR_ERROR)), screen_only=True )
# When using -vv or higher, simply do the default action else: CallbackModule = CallbackModule_dense |