1# (c) 2017 Ansible Project 2# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 4# Make coding more python3-ish 5from __future__ import (absolute_import, division, print_function) 6__metaclass__ = type 7 8DOCUMENTATION = ''' 9 callback: yaml 10 type: stdout 11 short_description: yaml-ized Ansible screen output 12 version_added: 2.5 13 description: 14 - Ansible output that can be quite a bit easier to read than the 15 default JSON formatting. 16 extends_documentation_fragment: 17 - default_callback 18 requirements: 19 - set as stdout in configuration 20''' 21 22import yaml 23import json 24import re 25import string 26import sys 27 28from ansible.module_utils._text import to_bytes, to_text 29from ansible.module_utils.six import string_types 30from ansible.parsing.yaml.dumper import AnsibleDumper 31from ansible.plugins.callback import CallbackBase, strip_internal_keys, module_response_deepcopy 32from ansible.plugins.callback.default import CallbackModule as Default 33 34 35# from http://stackoverflow.com/a/15423007/115478 36def should_use_block(value): 37 """Returns true if string should be in block format""" 38 for c in u"\u000a\u000d\u001c\u001d\u001e\u0085\u2028\u2029": 39 if c in value: 40 return True 41 return False 42 43 44def my_represent_scalar(self, tag, value, style=None): 45 """Uses block style for multi-line strings""" 46 if style is None: 47 if should_use_block(value): 48 style = '|' 49 # we care more about readable than accuracy, so... 50 # ...no trailing space 51 value = value.rstrip() 52 # ...and non-printable characters 53 value = ''.join(x for x in value if x in string.printable) 54 # ...tabs prevent blocks from expanding 55 value = value.expandtabs() 56 # ...and odd bits of whitespace 57 value = re.sub(r'[\x0b\x0c\r]', '', value) 58 # ...as does trailing space 59 value = re.sub(r' +\n', '\n', value) 60 else: 61 style = self.default_style 62 node = yaml.representer.ScalarNode(tag, value, style=style) 63 if self.alias_key is not None: 64 self.represented_objects[self.alias_key] = node 65 return node 66 67 68class CallbackModule(Default): 69 70 """ 71 Variation of the Default output which uses nicely readable YAML instead 72 of JSON for printing results. 73 """ 74 75 CALLBACK_VERSION = 2.0 76 CALLBACK_TYPE = 'stdout' 77 CALLBACK_NAME = 'yaml' 78 79 def __init__(self): 80 super(CallbackModule, self).__init__() 81 yaml.representer.BaseRepresenter.represent_scalar = my_represent_scalar 82 83 def _dump_results(self, result, indent=None, sort_keys=True, keep_invocation=False): 84 if result.get('_ansible_no_log', False): 85 return json.dumps(dict(censored="The output has been hidden due to the fact that 'no_log: true' was specified for this result")) 86 87 # All result keys stating with _ansible_ are internal, so remove them from the result before we output anything. 88 abridged_result = strip_internal_keys(module_response_deepcopy(result)) 89 90 # remove invocation unless specifically wanting it 91 if not keep_invocation and self._display.verbosity < 3 and 'invocation' in result: 92 del abridged_result['invocation'] 93 94 # remove diff information from screen output 95 if self._display.verbosity < 3 and 'diff' in result: 96 del abridged_result['diff'] 97 98 # remove exception from screen output 99 if 'exception' in abridged_result: 100 del abridged_result['exception'] 101 102 dumped = '' 103 104 # put changed and skipped into a header line 105 if 'changed' in abridged_result: 106 dumped += 'changed=' + str(abridged_result['changed']).lower() + ' ' 107 del abridged_result['changed'] 108 109 if 'skipped' in abridged_result: 110 dumped += 'skipped=' + str(abridged_result['skipped']).lower() + ' ' 111 del abridged_result['skipped'] 112 113 # if we already have stdout, we don't need stdout_lines 114 if 'stdout' in abridged_result and 'stdout_lines' in abridged_result: 115 abridged_result['stdout_lines'] = '<omitted>' 116 117 # if we already have stderr, we don't need stderr_lines 118 if 'stderr' in abridged_result and 'stderr_lines' in abridged_result: 119 abridged_result['stderr_lines'] = '<omitted>' 120 121 if abridged_result: 122 dumped += '\n' 123 dumped += to_text(yaml.dump(abridged_result, allow_unicode=True, width=1000, Dumper=AnsibleDumper, default_flow_style=False)) 124 125 # indent by a couple of spaces 126 dumped = '\n '.join(dumped.split('\n')).rstrip() 127 return dumped 128 129 def _serialize_diff(self, diff): 130 return to_text(yaml.dump(diff, allow_unicode=True, width=1000, Dumper=AnsibleDumper, default_flow_style=False)) 131