tools/qvm-ls: assume "unknown" colums being VM properties

Listing VM properties require specific object, so instead of collecting
all properties from all VMs (which may be denied by policy), simply
try to access properties (and display '-' when it fails).

QubesOS/qubes-issues#853
This commit is contained in:
Marek Marczykowski-Górecki 2017-02-28 01:38:54 +01:00
parent 77250ab529
commit 554081498d
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724

View File

@ -111,12 +111,6 @@ class Column(object):
if ret is None: if ret is None:
return None return None
# late import to avoid circular import
# pylint: disable=redefined-outer-name
import qubes.vm
if isinstance(ret, (qubes.vm.BaseVM, qubes.Label)):
return ret.name
if isinstance(ret, int): if isinstance(ret, int):
return str(ret) return str(ret)
@ -135,92 +129,33 @@ class Column(object):
return self.ls_head < other.ls_head return self.ls_head < other.ls_head
def column(width=0, head=None):
'''Mark function or plain property as valid column in :program:`qvm-ls`.
By default all instances of :py:class:`qubes.property` are valid.
:param int width: Column width
:param str head: Column head (default: take property's name)
'''
def decorator(obj):
# pylint: disable=missing-docstring
# we keep hints on fget, so the order of decorators does not matter
holder = obj.fget if isinstance(obj, property) else obj
try:
holder.ls_head = head or holder.__name__.replace('_', '-').upper()
except AttributeError:
raise TypeError('Cannot find default column name '
'for a strange object {!r}'.format(obj))
holder.ls_width = max(width, len(holder.ls_head) + 1)
return obj
return decorator
class PropertyColumn(Column): class PropertyColumn(Column):
'''Column that displays value from property (:py:class:`property` or '''Column that displays value from property (:py:class:`property` or
:py:class:`qubes.property`) of domain. :py:class:`qubes.property`) of domain.
You shouldn't use this class directly, see :py:func:`column` decorator. :param name: Name of VM property.
:param holder: Holder of magic attributes.
''' '''
def __init__(self, holder): def __init__(self, name):
ls_head = name.replace('_', '-').upper()
super(PropertyColumn, self).__init__( super(PropertyColumn, self).__init__(
head=holder.ls_head, head=ls_head,
width=holder.ls_width, attr=name)
attr=holder.__name__,
doc=holder.__doc__)
self.holder = holder
def __repr__(self): def __repr__(self):
return '{}(head={!r}, width={!r} holder={!r})'.format( return '{}(head={!r}'.format(
self.__class__.__name__, self.__class__.__name__,
self.ls_head, self.ls_head)
self.ls_width,
self.holder)
def process_class(cls): def process_vm(vm):
'''Process class after definition to find all listable properties. '''Process VM object to find all listable properties.
It is used in metaclass of the domain. :param qubesmgmt.vm.QubesVM vm: VM object.
:param qubes.vm.BaseVMMeta cls: Class to round up.
''' '''
for klass in cls.__mro__: for prop_name in vm.property_list():
for prop in klass.__dict__.values(): PropertyColumn(prop_name)
holder = prop.fget \
if isinstance(prop, property) \
else prop
if not hasattr(holder, 'ls_head') or holder.ls_head is None:
continue
for col in Column.columns.values():
if not isinstance(col, PropertyColumn):
continue
if col.holder.__name__ != holder.__name__:
continue
if col.ls_head != holder.ls_head:
raise TypeError('Found column head mismatch in class {!r} '
'({!r} != {!r})'.format(cls.__name__,
holder.ls_head, col.ls_head))
if col.ls_width != holder.ls_width:
raise TypeError('Found column width mismatch in class {!r} '
'({!r} != {!r})'.format(cls.__name__,
holder.ls_width, col.ls_width))
PropertyColumn(holder)
def flag(field): def flag(field):
@ -623,6 +558,11 @@ def main(args=None):
else: else:
columns = formats[args.format] columns = formats[args.format]
# assume unknown columns are VM properties
for col in columns:
if col.upper() not in Column.columns:
PropertyColumn(col)
table = Table(args.app, columns) table = Table(args.app, columns)
table.write_table(sys.stdout) table.write_table(sys.stdout)