tools/qvm-ls: kill fixed column width

Calculate column width dynamically, besed on actual contents.
This commit is contained in:
Marek Marczykowski-Górecki 2017-03-11 01:47:12 +01:00
parent fea3d3391f
commit 435a465d70
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
2 changed files with 35 additions and 38 deletions

View File

@ -497,7 +497,7 @@ class VmNameGroup(argparse._MutuallyExclusiveGroup):
self.add_argument('VMNAME', action=vm_action, nargs='*', default=[]) self.add_argument('VMNAME', action=vm_action, nargs='*', default=[])
def print_table(table): def print_table(table, stream=None):
''' Uses the unix column command to print pretty table. ''' Uses the unix column command to print pretty table.
:param str text: list of lists/sets :param str text: list of lists/sets
@ -507,13 +507,16 @@ def print_table(table):
text_table = '\n'.join([unit_separator.join(row) for row in table]) text_table = '\n'.join([unit_separator.join(row) for row in table])
text_table += '\n' text_table += '\n'
if stream is None:
stream = sys.stdout
# for tests... # for tests...
if sys.stdout != sys.__stdout__: if stream != sys.__stdout__:
p = subprocess.Popen(cmd + ['-c', '80'], stdin=subprocess.PIPE, p = subprocess.Popen(cmd + ['-c', '80'], stdin=subprocess.PIPE,
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
p.stdin.write(text_table.encode()) p.stdin.write(text_table.encode())
(out, _) = p.communicate() (out, _) = p.communicate()
sys.stdout.write(out.decode()) stream.write(out.decode())
else: else:
p = subprocess.Popen(cmd, stdin=subprocess.PIPE) p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
p.communicate(text_table.encode()) p.communicate(text_table.encode())

View File

@ -41,11 +41,10 @@ import qubesmgmt.vm
# #
class Column(object): class Column(object):
'''A column in qvm-ls output characterised by its head, a width and a way '''A column in qvm-ls output characterised by its head and a way
to fetch a parameter describing the domain. to fetch a parameter describing the domain.
:param str head: Column head (usually uppercase). :param str head: Column head (usually uppercase).
:param int width: Column width.
:param str attr: Attribute, possibly complex (containing ``.``). This may \ :param str attr: Attribute, possibly complex (containing ``.``). This may \
also be a callable that gets as its only argument the domain. also be a callable that gets as its only argument the domain.
:param str doc: Description of column (will be visible in --help-columns). :param str doc: Description of column (will be visible in --help-columns).
@ -54,9 +53,8 @@ class Column(object):
#: collection of all columns #: collection of all columns
columns = {} columns = {}
def __init__(self, head, width=0, attr=None, doc=None): def __init__(self, head, attr=None, doc=None):
self.ls_head = head self.ls_head = head
self.ls_width = max(width, len(self.ls_head) + 1)
self.__doc__ = doc if doc is None else qubesmgmt.utils.format_doc(doc) self.__doc__ = doc if doc is None else qubesmgmt.utils.format_doc(doc)
# intentionally not always do set self._attr, # intentionally not always do set self._attr,
@ -77,12 +75,12 @@ class Column(object):
:py:meth:`Column.format` method instead. :py:meth:`Column.format` method instead.
:param qubes.vm.qubesvm.QubesVM: Domain to get a value from. :param qubes.vm.qubesvm.QubesVM: Domain to get a value from.
:returns: string that is at least as wide as needed to fill table row. :returns: string to display
:rtype: str :rtype: str
''' '''
value = self.format(vm) or '-' value = self.format(vm) or '-'
return value.ljust(self.ls_width) return value
def format(self, vm): def format(self, vm):
@ -117,8 +115,8 @@ class Column(object):
return ret return ret
def __repr__(self): def __repr__(self):
return '{}(head={!r}, width={!r})'.format(self.__class__.__name__, return '{}(head={!r})'.format(self.__class__.__name__,
self.ls_head, self.ls_width) self.ls_head)
def __eq__(self, other): def __eq__(self, other):
@ -200,7 +198,6 @@ class StatusColumn(Column):
def __init__(self): def __init__(self):
super(StatusColumn, self).__init__( super(StatusColumn, self).__init__(
head='STATUS', head='STATUS',
width=len(self.get_flags()) + 1,
doc=self.__class__.__doc__) doc=self.__class__.__doc__)
@ -337,41 +334,41 @@ def calc_used(vm, volume_name):
# todo maxmem # todo maxmem
Column('GATEWAY', width=15, Column('GATEWAY',
attr='netvm.gateway', attr='netvm.gateway',
doc='Network gateway.') doc='Network gateway.')
Column('MEMORY', width=5, Column('MEMORY',
attr=(lambda vm: vm.get_mem() / 1024 if vm.is_running() else None), attr=(lambda vm: vm.get_mem() / 1024 if vm.is_running() else None),
doc='Memory currently used by VM') doc='Memory currently used by VM')
Column('DISK', width=5, Column('DISK',
attr=(lambda vm: vm.storage.get_disk_utilization() / 1024 / 1024), attr=(lambda vm: vm.storage.get_disk_utilization() / 1024 / 1024),
doc='Total disk utilisation.') doc='Total disk utilisation.')
Column('PRIV-CURR', width=5, Column('PRIV-CURR',
attr=(lambda vm: calc_usage(vm, 'private')), attr=(lambda vm: calc_usage(vm, 'private')),
doc='Disk utilisation by private image (/home, /usr/local).') doc='Disk utilisation by private image (/home, /usr/local).')
Column('PRIV-MAX', width=5, Column('PRIV-MAX',
attr=(lambda vm: calc_size(vm, 'private')), attr=(lambda vm: calc_size(vm, 'private')),
doc='Maximum available space for private image.') doc='Maximum available space for private image.')
Column('PRIV-USED', width=5, Column('PRIV-USED',
attr=(lambda vm: calc_used(vm, 'private')), attr=(lambda vm: calc_used(vm, 'private')),
doc='Disk utilisation by private image as a percentage of available space.') doc='Disk utilisation by private image as a percentage of available space.')
Column('ROOT-CURR', width=5, Column('ROOT-CURR',
attr=(lambda vm: calc_usage(vm, 'root')), attr=(lambda vm: calc_usage(vm, 'root')),
doc='Disk utilisation by root image (/usr, /lib, /etc, ...).') doc='Disk utilisation by root image (/usr, /lib, /etc, ...).')
Column('ROOT-MAX', width=5, Column('ROOT-MAX',
attr=(lambda vm: calc_size(vm, 'root')), attr=(lambda vm: calc_size(vm, 'root')),
doc='Maximum available space for root image.') doc='Maximum available space for root image.')
Column('ROOT-USED', width=5, Column('ROOT-USED',
attr=(lambda vm: calc_used(vm, 'root')), attr=(lambda vm: calc_used(vm, 'root')),
doc='Disk utilisation by root image as a percentage of available space.') doc='Disk utilisation by root image as a percentage of available space.')
@ -391,21 +388,13 @@ class Table(object):
self.raw_data = raw_data self.raw_data = raw_data
def format_head(self): def get_head(self):
'''Format table head (all column heads).''' '''Get table head data (all column heads).'''
return ''.join('{head:{width}s}'.format( return [col.ls_head for col in self.columns]
head=col.ls_head, width=col.ls_width)
for col in self.columns[:-1]) + \
self.columns[-1].ls_head
def format_row(self, vm):
'''Format single table row (all columns for one domain).'''
if self.raw_data:
return '|'.join(col.format(vm) for col in self.columns)
else:
return ''.join(col.cell(vm) for col in self.columns)
def get_row(self, vm):
'''Get single table row data (all columns for one domain).'''
return [col.cell(vm) for col in self.columns]
def write_table(self, stream=sys.stdout): def write_table(self, stream=sys.stdout):
'''Write whole table to file-like object. '''Write whole table to file-like object.
@ -413,10 +402,15 @@ class Table(object):
:param file stream: Stream to write the table to. :param file stream: Stream to write the table to.
''' '''
table_data = []
if not self.raw_data: if not self.raw_data:
stream.write(self.format_head() + '\n') table_data.append(self.get_head())
for vm in self.app.domains: for vm in self.app.domains:
stream.write(self.format_row(vm) + '\n') table_data.append(self.get_row(vm))
qubesmgmt.tools.print_table(table_data, stream=stream)
else:
for vm in self.app.domains:
stream.write('|'.join(self.get_row(vm)) + '\n')
#: Available formats. Feel free to plug your own one. #: Available formats. Feel free to plug your own one.