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:
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):
return str(ret)
@ -135,92 +129,33 @@ class Column(object):
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):
'''Column that displays value from property (:py:class:`property` or
:py:class:`qubes.property`) of domain.
You shouldn't use this class directly, see :py:func:`column` decorator.
:param holder: Holder of magic attributes.
:param name: Name of VM property.
'''
def __init__(self, holder):
def __init__(self, name):
ls_head = name.replace('_', '-').upper()
super(PropertyColumn, self).__init__(
head=holder.ls_head,
width=holder.ls_width,
attr=holder.__name__,
doc=holder.__doc__)
self.holder = holder
head=ls_head,
attr=name)
def __repr__(self):
return '{}(head={!r}, width={!r} holder={!r})'.format(
return '{}(head={!r}'.format(
self.__class__.__name__,
self.ls_head,
self.ls_width,
self.holder)
self.ls_head)
def process_class(cls):
'''Process class after definition to find all listable properties.
def process_vm(vm):
'''Process VM object to find all listable properties.
It is used in metaclass of the domain.
:param qubes.vm.BaseVMMeta cls: Class to round up.
:param qubesmgmt.vm.QubesVM vm: VM object.
'''
for klass in cls.__mro__:
for prop in klass.__dict__.values():
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)
for prop_name in vm.property_list():
PropertyColumn(prop_name)
def flag(field):
@ -623,6 +558,11 @@ def main(args=None):
else:
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.write_table(sys.stdout)