%PDF- %PDF-
Direktori : /usr/lib/python2.7/site-packages/salt/output/ |
Current File : //usr/lib/python2.7/site-packages/salt/output/table_out.py |
# -*- coding: utf-8 -*- ''' Display output in a table format ================================= .. versionadded:: 2017.7.0 This outputter displays a sequence of rows as table. Example output:: edge01.bjm01: ---------- comment: ---------- out: ---------- ______________________________________________________________________________ | Active | Interface | Last Move | Mac | Moves | Static | Vlan | ______________________________________________________________________________ | True | ae1.900 | 0.0 | 40:A6:77:5A:50:01 | 0 | False | 111 | ______________________________________________________________________________ | True | ae1.111 | 0.0 | 64:16:8D:32:26:58 | 0 | False | 111 | ______________________________________________________________________________ | True | ae1.111 | 0.0 | 8C:60:4F:73:2D:57 | 0 | False | 111 | ______________________________________________________________________________ | True | ae1.111 | 0.0 | 8C:60:4F:73:2D:7C | 0 | False | 111 | ______________________________________________________________________________ | True | ae1.222 | 0.0 | 8C:60:4F:73:2D:57 | 0 | False | 222 | ______________________________________________________________________________ | True | ae1.222 | 0.0 | F4:0F:1B:76:9D:97 | 0 | False | 222 | ______________________________________________________________________________ result: ---------- ''' # Import Python libs from __future__ import absolute_import, print_function, unicode_literals import operator from functools import reduce # pylint: disable=redefined-builtin # Import Salt libs import salt.output import salt.utils.color import salt.utils.data # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import map, zip # pylint: disable=redefined-builtin __virtualname__ = 'table' def __virtual__(): return __virtualname__ class TableDisplay(object): ''' Manage the table display content. ''' _JUSTIFY_MAP = { 'center': six.text_type.center, 'right': six.text_type.rjust, 'left': six.text_type.ljust } def __init__(self, has_header=True, # if header will be displayed row_delimiter='-', # row delimiter char delim=' | ', # column delimiter justify='center', # text justify separate_rows=True, # display the line separating two consecutive rows prefix='| ', # character to display at the beginning of the row suffix=' |', # character to display at the end of the row width=50, # column max width wrapfunc=None): # function wrapper self.__dict__.update( salt.utils.color.get_colors( __opts__.get('color'), __opts__.get('color_theme') ) ) self.strip_colors = __opts__.get('strip_colors', True) self.has_header = has_header self.row_delimiter = row_delimiter self.delim = delim self.justify = justify self.separate_rows = separate_rows self.prefix = prefix self.suffix = suffix self.width = width if not(wrapfunc and callable(wrapfunc)): self.wrapfunc = self.wrap_onspace else: self.wrapfunc = wrapfunc def ustring(self, indent, color, msg, prefix='', suffix='', endc=None): '''Build the unicode string to be displayed.''' if endc is None: endc = self.ENDC # pylint: disable=no-member indent *= ' ' fmt = u'{0}{1}{2}{3}{4}{5}' try: return fmt.format(indent, color, prefix, msg, endc, suffix) except UnicodeDecodeError: return fmt.format(indent, color, prefix, salt.utils.data.decode(msg), endc, suffix) def wrap_onspace(self, text): ''' When the text inside the column is longer then the width, will split by space and continue on the next line.''' def _truncate(line, word): return '{line}{part}{word}'.format( line=line, part=' \n'[(len(line[line.rfind('\n')+1:]) + len(word.split('\n', 1)[0]) >= self.width)], word=word ) return reduce(_truncate, text.split(' ')) def prepare_rows(self, rows, indent, has_header): '''Prepare rows content to be displayed.''' out = [] def row_wrapper(row): new_rows = [ self.wrapfunc(item).split('\n') for item in row ] rows = [] for item in map(lambda *args: args, *new_rows): if isinstance(item, (tuple, list)): rows.append([substr or '' for substr in item]) else: rows.append([item]) return rows logical_rows = [ row_wrapper(row) for row in rows ] columns = map(lambda *args: args, *reduce(operator.add, logical_rows)) max_widths = [ max([len(six.text_type(item)) for item in column]) for column in columns ] row_separator = self.row_delimiter * (len(self.prefix) + len(self.suffix) + sum(max_widths) + len(self.delim) * (len(max_widths) - 1)) justify = self._JUSTIFY_MAP[self.justify.lower()] if self.separate_rows: out.append( self.ustring( indent, self.LIGHT_GRAY, # pylint: disable=no-member row_separator ) ) for physical_rows in logical_rows: for row in physical_rows: line = self.prefix \ + self.delim.join([ justify(six.text_type(item), width) for (item, width) in zip(row, max_widths) ]) + self.suffix out.append( self.ustring( indent, self.WHITE, # pylint: disable=no-member line ) ) if self.separate_rows or has_header: out.append( self.ustring( indent, self.LIGHT_GRAY, # pylint: disable=no-member row_separator ) ) has_header = False return out def display_rows(self, rows, labels, indent): '''Prepares row content and displays.''' out = [] if not rows: return out first_row_type = type(rows[0]) # all rows must have the same datatype consistent = True for row in rows[1:]: if type(row) != first_row_type: consistent = False if not consistent: return out if isinstance(labels, dict): labels_temp = [] for key in sorted(labels): labels_temp.append(labels[key]) labels = labels_temp if first_row_type is dict: # and all the others temp_rows = [] if not labels: labels = [six.text_type(label).replace('_', ' ').title() for label in sorted(rows[0])] for row in rows: temp_row = [] for key in sorted(row): temp_row.append(six.text_type(row[key])) temp_rows.append(temp_row) rows = temp_rows elif isinstance(rows[0], six.string_types): rows = [[row] for row in rows] # encapsulate each row in a single-element list labels_and_rows = [labels] + rows if labels else rows has_header = self.has_header and labels return self.prepare_rows(labels_and_rows, indent + 4, has_header) def display(self, ret, indent, out, rows_key=None, labels_key=None): '''Display table(s).''' rows = [] labels = None if isinstance(ret, dict): if not rows_key or (rows_key and rows_key in list(ret.keys())): # either not looking for a specific key # either looking and found in the current root for key in sorted(ret): if rows_key and key != rows_key: continue # if searching specifics, ignore anything else val = ret[key] if not rows_key: out.append( self.ustring( indent, self.DARK_GRAY, # pylint: disable=no-member key, suffix=':' ) ) out.append( self.ustring( indent, self.DARK_GRAY, # pylint: disable=no-member '----------' ) ) if isinstance(val, (list, tuple)): rows = val if labels_key: # at the same depth labels = ret.get(labels_key) # if any out.extend(self.display_rows(rows, labels, indent)) else: self.display(val, indent + 4, out, rows_key=rows_key, labels_key=labels_key) elif rows_key: # dig deeper for key in sorted(ret): val = ret[key] self.display(val, indent, out, rows_key=rows_key, labels_key=labels_key) # same indent elif isinstance(ret, (list, tuple)): if not rows_key: rows = ret out.extend(self.display_rows(rows, labels, indent)) return out def output(ret, **kwargs): ''' Display the output as table. Args: * nested_indent: integer, specify the left alignment. * has_header: boolean specifying if header should be displayed. Default: True. * row_delimiter: character to separate rows. Default: ``_``. * delim: character to separate columns. Default: ``" | "``. * justify: text alignment. Default: ``center``. * separate_rows: boolean specifying if row separator will be displayed between consecutive rows. Default: True. * prefix: character at the beginning of the row. Default: ``"| "``. * suffix: character at the end of the row. Default: ``" |"``. * width: column max width. Default: ``50``. * rows_key: display the rows under a specific key. * labels_key: use the labels under a certain key. Otherwise will try to use the dictionary keys (if any). * title: display title when only one table is selected (using the ``rows_key`` argument). ''' # to facilitate re-use if 'opts' in kwargs: global __opts__ # pylint: disable=W0601 __opts__ = kwargs.pop('opts') # Prefer kwargs before opts base_indent = kwargs.get('nested_indent', 0) \ or __opts__.get('out.table.nested_indent', 0) rows_key = kwargs.get('rows_key') \ or __opts__.get('out.table.rows_key') labels_key = kwargs.get('labels_key') \ or __opts__.get('out.table.labels_key') title = kwargs.get('title') \ or __opts__.get('out.table.title') class_kvargs = {} argks = ('has_header', 'row_delimiter', 'delim', 'justify', 'separate_rows', 'prefix', 'suffix', 'width') for argk in argks: argv = kwargs.get(argk) \ or __opts__.get('out.table.{key}'.format(key=argk)) if argv is not None: class_kvargs[argk] = argv table = TableDisplay(**class_kvargs) out = [] if title and rows_key: out.append( table.ustring( base_indent, title, table.WHITE, # pylint: disable=no-member suffix='\n' ) ) return '\n'.join(table.display(salt.utils.data.decode(ret), base_indent, out, rows_key=rows_key, labels_key=labels_key))