Skip to content

Commit

Permalink
tools/qvm-ls: assume "unknown" colums being VM properties
Browse files Browse the repository at this point in the history
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
  • Loading branch information
marmarek committed Mar 11, 2017
1 parent 77250ab commit 5540814
Showing 1 changed file with 17 additions and 77 deletions.
94 changes: 17 additions & 77 deletions qubesmgmt/tools/qvm_ls.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,6 @@ def format(self, vm):
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)

Expand All @@ -135,92 +129,33 @@ def __lt__(self, other):
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.

It is used in metaclass of the domain.
def process_vm(vm):
'''Process VM object to find all listable properties.
: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):
Expand Down Expand Up @@ -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)

Expand Down

0 comments on commit 5540814

Please sign in to comment.