tools: remove qvm-ls tool and related integration in qubes.property

qvm-ls tool (as all other tools) will be accessing properties through
API, so no need (nor sense) for this tool-specific attributes in
qubes.property. The only somehow used was ls_width, and in fact it made
the output unnecessary wide.

The tool itself is already moved to core-mgmt-client repository.

QubesOS/qubes-issues#853
This commit is contained in:
Marek Marczykowski-Górecki 2017-04-10 03:39:11 +02:00
parent 654e64c4a6
commit 2aa0de3d5b
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
10 changed files with 2 additions and 747 deletions

View File

@ -132,7 +132,7 @@ class Label(object):
self.icon_dispvm) + ".png" self.icon_dispvm) + ".png"
class property(object): # pylint: disable=redefined-builtin,invalid-name class property(object): # pylint: disable=redefined-builtin,invalid-name
'''Qubes property. '''Qubes property.
This class holds one property that can be saved to and loaded from This class holds one property that can be saved to and loaded from
@ -157,8 +157,6 @@ class property(object): # pylint: disable=redefined-builtin,invalid-name
:param int order: order of evaluation (bigger order values are later) :param int order: order of evaluation (bigger order values are later)
:param bool clone: :py:meth:`PropertyHolder.clone_properties` will not \ :param bool clone: :py:meth:`PropertyHolder.clone_properties` will not \
include this property by default if :py:obj:`False` include this property by default if :py:obj:`False`
:param str ls_head: column head for :program:`qvm-ls`
:param int ls_width: column width in :program:`qvm-ls`
:param str doc: docstring; this should be one paragraph of plain RST, no \ :param str doc: docstring; this should be one paragraph of plain RST, no \
sphinx-specific features sphinx-specific features
@ -192,7 +190,7 @@ class property(object): # pylint: disable=redefined-builtin,invalid-name
def __init__(self, name, setter=None, saver=None, type=None, def __init__(self, name, setter=None, saver=None, type=None,
default=_NO_DEFAULT, write_once=False, load_stage=2, order=0, default=_NO_DEFAULT, write_once=False, load_stage=2, order=0,
save_via_ref=False, clone=True, save_via_ref=False, clone=True,
ls_head=None, ls_width=None, doc=None): doc=None):
# pylint: disable=redefined-builtin # pylint: disable=redefined-builtin
self.__name__ = name self.__name__ = name
self._setter = setter self._setter = setter
@ -208,11 +206,6 @@ class property(object): # pylint: disable=redefined-builtin,invalid-name
self.__doc__ = doc self.__doc__ = doc
self._attr_name = '_qubesprop_' + name self._attr_name = '_qubesprop_' + name
if ls_head is not None or ls_width is not None:
self.ls_head = ls_head or self.__name__.replace('_', '-').upper()
self.ls_width = max(ls_width or 0, len(self.ls_head) + 1)
def __get__(self, instance, owner): def __get__(self, instance, owner):
if instance is None: if instance is None:
return self return self

View File

@ -908,7 +908,6 @@ def load_tests(loader, tests, pattern): # pylint: disable=unused-argument
'qubes.tests.mgmt', 'qubes.tests.mgmt',
'qubes.tests.tools.qvm_device', 'qubes.tests.tools.qvm_device',
'qubes.tests.tools.qvm_firewall', 'qubes.tests.tools.qvm_firewall',
'qubes.tests.tools.qvm_ls',
'qubespolicy.tests', 'qubespolicy.tests',
): ):
tests.addTests(loader.loadTestsFromName(modname)) tests.addTests(loader.loadTestsFromName(modname))

View File

@ -1,76 +0,0 @@
# pylint: disable=protected-access,pointless-statement
#
# The Qubes OS Project, https://www.qubes-os.org/
#
# Copyright (C) 2015 Joanna Rutkowska <joanna@invisiblethingslab.com>
# Copyright (C) 2015 Wojtek Porczyk <woju@invisiblethingslab.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
import qubes
import qubes.vm.adminvm
import qubes.tools.qvm_ls
import qubes.tests
import qubes.tests.vm
class TC_00_Column(qubes.tests.QubesTestCase):
def test_000_collected(self):
self.assertIn('NAME', qubes.tools.qvm_ls.Column.columns)
def test_100_init(self):
try:
testcolumn = qubes.tools.qvm_ls.Column('TESTCOLUMN', width=50)
self.assertEqual(testcolumn.ls_head, 'TESTCOLUMN')
self.assertEqual(testcolumn.ls_width, 50)
finally:
try:
qubes.tools.qvm_ls.Column.columns['TESTCOLUMN']
except KeyError:
pass
def test_101_fix_width(self):
try:
testcolumn = qubes.tools.qvm_ls.Column('TESTCOLUMN', width=2)
self.assertGreater(testcolumn.ls_width, len('TESTCOLUMN'))
finally:
try:
qubes.tools.qvm_ls.Column.columns['TESTCOLUMN']
except KeyError:
pass
class TC_90_globals(qubes.tests.QubesTestCase):
# @qubes.tests.skipUnlessDom0
def test_100_simple_flag(self):
flag = qubes.tools.qvm_ls.simple_flag(1, 'T', 'internal')
# TODO after serious testing of QubesVM and Qubes app, this should be
# using normal components
app = qubes.tests.vm.TestApp()
vm = qubes.vm.adminvm.AdminVM(app, None,
qid=0, name='dom0', internal='False')
self.assertFalse(flag(None, vm))
vm.internal = True
self.assertTrue(flag(None, vm))
def test_900_formats_columns(self):
for fmt in qubes.tools.qvm_ls.formats:
for col in qubes.tools.qvm_ls.formats[fmt]:
self.assertIn(col.upper(), qubes.tools.qvm_ls.Column.columns)

View File

@ -1,634 +0,0 @@
# pylint: disable=too-few-public-methods
#
# The Qubes OS Project, https://www.qubes-os.org/
#
# Copyright (C) 2015 Joanna Rutkowska <joanna@invisiblethingslab.com>
# Copyright (C) 2015 Wojtek Porczyk <woju@invisiblethingslab.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
'''qvm-ls - List available domains'''
from __future__ import print_function
import argparse
import collections
import sys
import textwrap
import qubes
import qubes.config
import qubes.tools
import qubes.utils
#
# columns
#
class Column(object):
'''A column in qvm-ls output characterised by its head, a width and a way
to fetch a parameter describing the domain.
:param str head: Column head (usually uppercase).
:param int width: Column width.
:param str attr: Attribute, possibly complex (containing ``.``). This may \
also be a callable that gets as its only argument the domain.
:param str doc: Description of column (will be visible in --help-columns).
'''
#: collection of all columns
columns = {}
def __init__(self, head, width=0, attr=None, doc=None):
self.ls_head = head
self.ls_width = max(width, len(self.ls_head) + 1)
self.__doc__ = doc if doc is None else qubes.utils.format_doc(doc)
# intentionally not always do set self._attr,
# to cause AttributeError in self.format()
if attr is not None:
self._attr = attr
self.__class__.columns[self.ls_head] = self
def cell(self, vm):
'''Format one cell.
.. note::
This is only for technical formatting (filling with space). If you
want to subclass the :py:class:`Column` class, you should override
:py:meth:`Column.format` method instead.
: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.
:rtype: str
'''
value = self.format(vm) or '-'
return value.ljust(self.ls_width)
def format(self, vm):
'''Format one cell value.
Return value to put in a table cell.
:param qubes.vm.qubesvm.QubesVM: Domain to get a value from.
:returns: Value to put, or :py:obj:`None` if no value.
:rtype: str or None
'''
ret = None
try:
if isinstance(self._attr, str):
ret = vm
for attrseg in self._attr.split('.'):
ret = getattr(ret, attrseg)
elif isinstance(self._attr, collections.Callable):
ret = self._attr(vm)
except (AttributeError, ZeroDivisionError):
# division by 0 may be caused by arithmetic in callable attr
return None
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)
return ret
def __repr__(self):
return '{}(head={!r}, width={!r})'.format(self.__class__.__name__,
self.ls_head, self.ls_width)
def __eq__(self, other):
return self.ls_head == other.ls_head
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.
'''
def __init__(self, holder):
super(PropertyColumn, self).__init__(
head=holder.ls_head,
width=holder.ls_width,
attr=holder.__name__,
doc=holder.__doc__)
self.holder = holder
def __repr__(self):
return '{}(head={!r}, width={!r} holder={!r})'.format(
self.__class__.__name__,
self.ls_head,
self.ls_width,
self.holder)
def process_class(cls):
'''Process class after definition to find all listable properties.
It is used in metaclass of the domain.
:param qubes.vm.BaseVMMeta cls: Class to round up.
'''
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)
def flag(field):
'''Mark method as flag field.
:param int field: Which field to fill (counted from 1)
'''
def decorator(obj):
# pylint: disable=missing-docstring
obj.field = field
return obj
return decorator
def simple_flag(field, letter, attr, doc=None):
'''Create simple, binary flag.
:param str attr: Attribute name to check. If result is true, flag is fired.
:param str letter: The letter to show.
'''
def helper(self, vm):
# pylint: disable=missing-docstring,unused-argument
try:
value = getattr(vm, attr)
except AttributeError:
value = False
if value:
return letter[0]
helper.__doc__ = doc
helper.field = field
return helper
class StatusColumn(Column):
'''Some fancy flags that describe general status of the domain.'''
# pylint: disable=no-self-use
def __init__(self):
super(StatusColumn, self).__init__(
head='STATUS',
width=len(self.get_flags()) + 1,
doc=self.__class__.__doc__)
@flag(1)
def type(self, vm):
'''Type of domain.
0 AdminVM (AKA Dom0)
aA AppVM
dD DisposableVM
sS StandaloneVM
tT TemplateVM
When it is HVM (optimised VM), the letter is capital.
'''
# late import because of circular dependency
# pylint: disable=redefined-outer-name
import qubes.vm
import qubes.vm.adminvm
import qubes.vm.appvm
import qubes.vm.dispvm
import qubes.vm.qubesvm
import qubes.vm.templatevm
if isinstance(vm, qubes.vm.adminvm.AdminVM):
return '0'
ret = None
# TODO right order, depending on inheritance
if isinstance(vm, qubes.vm.templatevm.TemplateVM):
ret = 't'
if isinstance(vm, qubes.vm.appvm.AppVM):
ret = 'a'
# if isinstance(vm, qubes.vm.standalonevm.StandaloneVM):
# ret = 's'
if isinstance(vm, qubes.vm.dispvm.DispVM):
ret = 'd'
if ret is not None:
if getattr(vm, 'hvm', False):
return ret.upper()
return ret
@flag(2)
def power(self, vm):
'''Current power state.
r running
t transient
p paused
s suspended
h halting
d dying
c crashed
? unknown
'''
state = vm.get_power_state().lower()
if state == 'unknown':
return '?'
elif state in ('running', 'transient', 'paused', 'suspended',
'halting', 'dying', 'crashed'):
return state[0]
updateable = simple_flag(3, 'U', 'updateable',
doc='If the domain is updateable.')
provides_network = simple_flag(4, 'N', 'provides_network',
doc='If the domain provides network.')
installed_by_rpm = simple_flag(5, 'R', 'installed_by_rpm',
doc='If the domain is installed by RPM.')
internal = simple_flag(6, 'i', 'internal',
doc='If the domain is internal (not normally shown, no appmenus).')
debug = simple_flag(7, 'D', 'debug',
doc='If the domain is being debugged.')
autostart = simple_flag(8, 'A', 'autostart',
doc='If the domain is marked for autostart.')
# TODO (not sure if really):
# include in backups
# uses_custom_config
def _no_flag(self, vm):
'''Reserved for future use.'''
@classmethod
def get_flags(cls):
'''Get all flags as list.
Holes between flags are filled with :py:meth:`_no_flag`.
:rtype: list
'''
flags = {}
for mycls in cls.__mro__:
for attr in mycls.__dict__.values():
if not hasattr(attr, 'field'):
continue
if attr.field in flags:
continue
flags[attr.field] = attr
return [(flags[i] if i in flags else cls._no_flag)
for i in range(1, max(flags) + 1)]
def format(self, vm):
return ''.join((flag(self, vm) or '-') for flag in self.get_flags())
def calc_size(vm, volume_name):
''' Calculates the volume size in MB '''
try:
return vm.volumes[volume_name].size // 1024 // 1024
except KeyError:
return 0
def calc_usage(vm, volume_name):
''' Calculates the volume usage in MB '''
try:
return vm.volumes[volume_name].usage // 1024 // 1024
except KeyError:
return 0
def calc_used(vm, volume_name):
''' Calculates the volume usage in percent '''
size = calc_size(vm, volume_name)
if size == 0:
return 0
usage = calc_usage(vm, volume_name)
return usage * 100 // size
# todo maxmem
Column('GATEWAY', width=15,
attr='netvm.gateway',
doc='Network gateway.')
Column('MEMORY', width=5,
attr=(lambda vm: vm.get_mem() / 1024 if vm.is_running() else None),
doc='Memory currently used by VM')
Column('DISK', width=5,
attr=(lambda vm: vm.storage.get_disk_utilization() / 1024 / 1024),
doc='Total disk utilisation.')
Column('PRIV-CURR', width=5,
attr=(lambda vm: calc_usage(vm, 'private')),
doc='Disk utilisation by private image (/home, /usr/local).')
Column('PRIV-MAX', width=5,
attr=(lambda vm: calc_size(vm, 'private')),
doc='Maximum available space for private image.')
Column('PRIV-USED', width=5,
attr=(lambda vm: calc_used(vm, 'private')),
doc='Disk utilisation by private image as a percentage of available space.')
Column('ROOT-CURR', width=5,
attr=(lambda vm: calc_usage(vm, 'root')),
doc='Disk utilisation by root image (/usr, /lib, /etc, ...).')
Column('ROOT-MAX', width=5,
attr=(lambda vm: calc_size(vm, 'root')),
doc='Maximum available space for root image.')
Column('ROOT-USED', width=5,
attr=(lambda vm: calc_used(vm, 'root')),
doc='Disk utilisation by root image as a percentage of available space.')
StatusColumn()
class Table(object):
'''Table that is displayed to the user.
:param qubes.Qubes app: Qubes application object.
:param list colnames: Names of the columns (need not to be uppercase).
'''
def __init__(self, app, colnames, raw_data=False):
self.app = app
self.columns = tuple(Column.columns[col.upper()] for col in colnames)
self.raw_data = raw_data
def format_head(self):
'''Format table head (all column heads).'''
return ''.join('{head:{width}s}'.format(
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)
return ''.join(col.cell(vm) for col in self.columns)
def write_table(self, stream=sys.stdout):
'''Write whole table to file-like object.
:param file stream: Stream to write the table to.
'''
if not self.raw_data:
stream.write(self.format_head() + '\n')
for vm in self.app.domains:
stream.write(self.format_row(vm) + '\n')
#: Available formats. Feel free to plug your own one.
formats = {
'simple': ('name', 'status', 'label', 'template', 'netvm'),
'network': ('name', 'status', 'netvm', 'ip', 'ipback', 'gateway'),
'full': ('name', 'status', 'label', 'qid', 'xid', 'uuid'),
# 'perf': ('name', 'status', 'cpu', 'memory'),
'disk': ('name', 'status', 'disk',
'priv-curr', 'priv-max', 'priv-used',
'root-curr', 'root-max', 'root-used'),
}
class _HelpColumnsAction(argparse.Action):
'''Action for argument parser that displays all columns and exits.'''
# pylint: disable=redefined-builtin
def __init__(self,
option_strings,
dest=argparse.SUPPRESS,
default=argparse.SUPPRESS,
help='list all available columns with short descriptions and exit'):
super(_HelpColumnsAction, self).__init__(
option_strings=option_strings,
dest=dest,
default=default,
nargs=0,
help=help)
def __call__(self, parser, namespace, values, option_string=None):
width = max(len(column.ls_head) for column in Column.columns.values())
wrapper = textwrap.TextWrapper(width=80,
initial_indent=' ', subsequent_indent=' ' * (width + 6))
text = 'Available columns:\n' + '\n'.join(
wrapper.fill('{head:{width}s} {doc}'.format(
head=column.ls_head,
doc=column.__doc__ or '',
width=width))
for column in sorted(Column.columns.values()))
parser.exit(message=text + '\n')
class _HelpFormatsAction(argparse.Action):
'''Action for argument parser that displays all formats and exits.'''
# pylint: disable=redefined-builtin
def __init__(self,
option_strings,
dest=argparse.SUPPRESS,
default=argparse.SUPPRESS,
help='list all available formats with their definitions and exit'):
super(_HelpFormatsAction, self).__init__(
option_strings=option_strings,
dest=dest,
default=default,
nargs=0,
help=help)
def __call__(self, parser, namespace, values, option_string=None):
width = max(len(fmt) for fmt in formats)
text = 'Available formats:\n' + ''.join(
' {fmt:{width}s} {columns}\n'.format(
fmt=fmt, columns=','.join(formats[fmt]).upper(), width=width)
for fmt in sorted(formats))
parser.exit(message=text)
def get_parser():
'''Create :py:class:`argparse.ArgumentParser` suitable for
:program:`qvm-ls`.
'''
# parser creation is delayed to get all the columns that are scattered
# thorough the modules
wrapper = textwrap.TextWrapper(width=80, break_on_hyphens=False,
initial_indent=' ', subsequent_indent=' ')
parser = qubes.tools.QubesArgumentParser(
formatter_class=argparse.RawTextHelpFormatter,
description='List Qubes domains and their parametres.',
epilog='available formats (see --help-formats):\n{}\n\n'
'available columns (see --help-columns):\n{}'.format(
wrapper.fill(', '.join(sorted(formats.keys()))),
wrapper.fill(', '.join(sorted(sorted(Column.columns.keys()))))))
parser.add_argument('--help-columns', action=_HelpColumnsAction)
parser.add_argument('--help-formats', action=_HelpFormatsAction)
parser_formats = parser.add_mutually_exclusive_group()
parser_formats.add_argument('--format', '-o', metavar='FORMAT',
action='store', choices=formats.keys(), default='simple',
help='preset format')
parser_formats.add_argument('--fields', '-O', metavar='FIELD,...',
action='store',
help='user specified format (see available columns below)')
parser.add_argument('--raw-data', action='store_true',
help='Display specify data of specified VMs. Intended for '
'bash-parsing.')
# parser.add_argument('--conf', '-c',
# action='store', metavar='CFGFILE',
# help='Qubes config file')
return parser
def main(args=None):
'''Main routine of :program:`qvm-ls`.
:param list args: Optional arguments to override those delivered from \
command line.
'''
parser = get_parser()
try:
args = parser.parse_args(args)
except qubes.exc.QubesException as e:
parser.print_error(str(e))
return 1
if args.fields:
columns = [col.strip() for col in args.fields.split(',')]
for col in columns:
if col.upper() not in Column.columns:
parser.error('no such column: {!r}'.format(col))
else:
columns = formats[args.format]
table = Table(args.app, columns)
table.write_table(sys.stdout)
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@ -38,7 +38,6 @@ import qubes
import qubes.devices import qubes.devices
import qubes.events import qubes.events
import qubes.log import qubes.log
import qubes.tools.qvm_ls
VM_ENTRY_POINT = 'qubes.vm' VM_ENTRY_POINT = 'qubes.vm'
@ -164,7 +163,6 @@ class BaseVMMeta(qubes.events.EmitterMeta):
'''Metaclass for :py:class:`.BaseVM`''' '''Metaclass for :py:class:`.BaseVM`'''
def __init__(cls, name, bases, dict_): def __init__(cls, name, bases, dict_):
super(BaseVMMeta, cls).__init__(name, bases, dict_) super(BaseVMMeta, cls).__init__(name, bases, dict_)
qubes.tools.qvm_ls.process_class(cls)
class BaseVM(qubes.PropertyHolder, metaclass=BaseVMMeta): class BaseVM(qubes.PropertyHolder, metaclass=BaseVMMeta):

View File

@ -34,7 +34,6 @@ class AppVM(qubes.vm.qubesvm.QubesVM):
template = qubes.VMProperty('template', template = qubes.VMProperty('template',
load_stage=4, load_stage=4,
vmclass=qubes.vm.templatevm.TemplateVM, vmclass=qubes.vm.templatevm.TemplateVM,
ls_width=31,
doc='Template, on which this AppVM is based.') doc='Template, on which this AppVM is based.')
dispvm_allowed = qubes.property('dispvm_allowed', dispvm_allowed = qubes.property('dispvm_allowed',

View File

@ -33,12 +33,10 @@ class DispVM(qubes.vm.qubesvm.QubesVM):
template = qubes.VMProperty('template', template = qubes.VMProperty('template',
load_stage=4, load_stage=4,
vmclass=qubes.vm.appvm.AppVM, vmclass=qubes.vm.appvm.AppVM,
ls_width=31,
doc='AppVM, on which this DispVM is based.') doc='AppVM, on which this DispVM is based.')
dispid = qubes.property('dispid', type=int, write_once=True, dispid = qubes.property('dispid', type=int, write_once=True,
clone=False, clone=False,
ls_width=3,
doc='''Internal, persistent identifier of particular DispVM.''') doc='''Internal, persistent identifier of particular DispVM.''')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

View File

@ -68,20 +68,17 @@ class NetVMMixin(qubes.events.Emitter):
mac = qubes.property('mac', type=str, mac = qubes.property('mac', type=str,
default='00:16:3E:5E:6C:00', default='00:16:3E:5E:6C:00',
setter=_setter_mac, setter=_setter_mac,
ls_width=17,
doc='MAC address of the NIC emulated inside VM') doc='MAC address of the NIC emulated inside VM')
ip = qubes.property('ip', type=str, ip = qubes.property('ip', type=str,
default=_default_ip, default=_default_ip,
setter=_setter_ip, setter=_setter_ip,
ls_width=15,
doc='IP address of this domain.') doc='IP address of this domain.')
# CORE2: swallowed uses_default_netvm # CORE2: swallowed uses_default_netvm
netvm = qubes.VMProperty('netvm', load_stage=4, allow_none=True, netvm = qubes.VMProperty('netvm', load_stage=4, allow_none=True,
default=(lambda self: self.app.default_fw_netvm if self.provides_network default=(lambda self: self.app.default_fw_netvm if self.provides_network
else self.app.default_netvm), else self.app.default_netvm),
ls_width=31,
doc='''VM that provides network connection to this domain. When doc='''VM that provides network connection to this domain. When
`None`, machine is disconnected. When absent, domain uses default `None`, machine is disconnected. When absent, domain uses default
NetVM.''') NetVM.''')
@ -101,21 +98,18 @@ class NetVMMixin(qubes.events.Emitter):
# #
@qubes.tools.qvm_ls.column(width=15)
@property @property
def visible_ip(self): def visible_ip(self):
'''IP address of this domain as seen by the domain.''' '''IP address of this domain as seen by the domain.'''
return self.features.check_with_template('net/fake-ip', None) or \ return self.features.check_with_template('net/fake-ip', None) or \
self.ip self.ip
@qubes.tools.qvm_ls.column(width=15)
@property @property
def visible_gateway(self): def visible_gateway(self):
'''Default gateway of this domain as seen by the domain.''' '''Default gateway of this domain as seen by the domain.'''
return self.features.check_with_template('net/fake-gateway', None) or \ return self.features.check_with_template('net/fake-gateway', None) or \
self.netvm.gateway self.netvm.gateway
@qubes.tools.qvm_ls.column(width=15)
@property @property
def visible_netmask(self): def visible_netmask(self):
'''Netmask as seen by the domain.''' '''Netmask as seen by the domain.'''
@ -139,13 +133,11 @@ class NetVMMixin(qubes.events.Emitter):
# does not happen, because qid < 253, but may happen in the future. # does not happen, because qid < 253, but may happen in the future.
return '10.137.{}.{}'.format((vm.qid >> 8) & 7, vm.qid & 7) return '10.137.{}.{}'.format((vm.qid >> 8) & 7, vm.qid & 7)
@qubes.tools.qvm_ls.column(head='IPBACK', width=15)
@property @property
def gateway(self): def gateway(self):
'''Gateway for other domains that use this domain as netvm.''' '''Gateway for other domains that use this domain as netvm.'''
return self.visible_ip if self.provides_network else None return self.visible_ip if self.provides_network else None
@qubes.tools.qvm_ls.column(width=15)
@property @property
def netmask(self): def netmask(self):
'''Netmask for gateway address.''' '''Netmask for gateway address.'''
@ -164,7 +156,6 @@ class NetVMMixin(qubes.events.Emitter):
# used in both # used in both
# #
@qubes.tools.qvm_ls.column(width=15)
@property @property
def dns(self): def dns(self):
'''Secondary DNS server set up for this domain.''' '''Secondary DNS server set up for this domain.'''

View File

@ -47,7 +47,6 @@ import qubes.exc
import qubes.storage import qubes.storage
import qubes.storage.domain import qubes.storage.domain
import qubes.storage.file import qubes.storage.file
import qubes.tools.qvm_ls
import qubes.utils import qubes.utils
import qubes.vm import qubes.vm
import qubes.vm.mix.net import qubes.vm.mix.net
@ -357,7 +356,6 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
label = qubes.property('label', label = qubes.property('label',
setter=_setter_label, setter=_setter_label,
saver=(lambda self, prop, value: 'label-{}'.format(value.index)), saver=(lambda self, prop, value: 'label-{}'.format(value.index)),
ls_width=14,
doc='''Colourful label assigned to VM. This is where the colour of the doc='''Colourful label assigned to VM. This is where the colour of the
padlock is set.''') padlock is set.''')
@ -368,18 +366,15 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
qid = qubes.property('qid', type=int, write_once=True, qid = qubes.property('qid', type=int, write_once=True,
setter=_setter_qid, setter=_setter_qid,
clone=False, clone=False,
ls_width=3,
doc='''Internal, persistent identificator of particular domain. Note doc='''Internal, persistent identificator of particular domain. Note
this is different from Xen domid.''') this is different from Xen domid.''')
name = qubes.property('name', type=str, name = qubes.property('name', type=str,
clone=False, clone=False,
ls_width=31,
doc='User-specified name of the domain.') doc='User-specified name of the domain.')
uuid = qubes.property('uuid', type=uuid.UUID, write_once=True, uuid = qubes.property('uuid', type=uuid.UUID, write_once=True,
clone=False, clone=False,
ls_width=36,
doc='UUID from libvirt.') doc='UUID from libvirt.')
hvm = qubes.property('hvm', hvm = qubes.property('hvm',
@ -416,14 +411,12 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
type=int, type=int,
setter=_setter_positive_int, setter=_setter_positive_int,
default=(lambda self: self.app.host.no_cpus), default=(lambda self: self.app.host.no_cpus),
ls_width=2,
doc='FIXME') doc='FIXME')
# CORE2: swallowed uses_default_kernel # CORE2: swallowed uses_default_kernel
kernel = qubes.property('kernel', type=str, kernel = qubes.property('kernel', type=str,
setter=_setter_kernel, setter=_setter_kernel,
default=(lambda self: self.app.default_kernel), default=(lambda self: self.app.default_kernel),
ls_width=12,
doc='Kernel used by this domain.') doc='Kernel used by this domain.')
# CORE2: swallowed uses_default_kernelopts # CORE2: swallowed uses_default_kernelopts
@ -434,7 +427,6 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
if list(self.devices['pci'].persistent()) if list(self.devices['pci'].persistent())
else self.template.kernelopts if hasattr(self, 'template') else self.template.kernelopts if hasattr(self, 'template')
else qubes.config.defaults['kernelopts']), else qubes.config.defaults['kernelopts']),
ls_width=30,
doc='Kernel command line passed to domain.') doc='Kernel command line passed to domain.')
debug = qubes.property('debug', type=bool, default=False, debug = qubes.property('debug', type=bool, default=False,
@ -449,7 +441,6 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
default=(lambda self: self.template.default_user default=(lambda self: self.template.default_user
if hasattr(self, 'template') else 'user'), if hasattr(self, 'template') else 'user'),
setter=_setter_default_user, setter=_setter_default_user,
ls_width=12,
doc='FIXME') doc='FIXME')
# pylint: enable=no-member # pylint: enable=no-member
@ -463,7 +454,6 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
qrexec_timeout = qubes.property('qrexec_timeout', type=int, default=60, qrexec_timeout = qubes.property('qrexec_timeout', type=int, default=60,
setter=_setter_positive_int, setter=_setter_positive_int,
ls_width=3,
doc='''Time in seconds after which qrexec connection attempt is deemed doc='''Time in seconds after which qrexec connection attempt is deemed
failed. Operating system inside VM should be able to boot in this failed. Operating system inside VM should be able to boot in this
time.''') time.''')
@ -512,7 +502,6 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
# VMM-related # VMM-related
@qubes.tools.qvm_ls.column(width=3)
@property @property
def xid(self): def xid(self):
'''Xen ID. '''Xen ID.

View File

@ -288,7 +288,6 @@ fi
%{python3_sitelib}/qubes/tools/qvm_check.py %{python3_sitelib}/qubes/tools/qvm_check.py
%{python3_sitelib}/qubes/tools/qvm_clone.py %{python3_sitelib}/qubes/tools/qvm_clone.py
%{python3_sitelib}/qubes/tools/qvm_kill.py %{python3_sitelib}/qubes/tools/qvm_kill.py
%{python3_sitelib}/qubes/tools/qvm_ls.py
%{python3_sitelib}/qubes/tools/qvm_pause.py %{python3_sitelib}/qubes/tools/qvm_pause.py
%{python3_sitelib}/qubes/tools/qvm_pool.py %{python3_sitelib}/qubes/tools/qvm_pool.py
%{python3_sitelib}/qubes/tools/qvm_prefs.py %{python3_sitelib}/qubes/tools/qvm_prefs.py
@ -349,7 +348,6 @@ fi
%{python3_sitelib}/qubes/tests/tools/init.py %{python3_sitelib}/qubes/tests/tools/init.py
%{python3_sitelib}/qubes/tests/tools/qvm_device.py %{python3_sitelib}/qubes/tests/tools/qvm_device.py
%{python3_sitelib}/qubes/tests/tools/qvm_firewall.py %{python3_sitelib}/qubes/tests/tools/qvm_firewall.py
%{python3_sitelib}/qubes/tests/tools/qvm_ls.py
%dir %{python3_sitelib}/qubes/tests/integ %dir %{python3_sitelib}/qubes/tests/integ
%dir %{python3_sitelib}/qubes/tests/integ/__pycache__ %dir %{python3_sitelib}/qubes/tests/integ/__pycache__