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:
parent
77250ab529
commit
554081498d
@ -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)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user