qubes: pylint fixes (mostly whitespace)

This commit is contained in:
Wojtek Porczyk 2015-01-19 17:06:30 +01:00
parent e3415a7b4e
commit cdc3df66c8
14 changed files with 407 additions and 234 deletions

View File

@ -123,9 +123,11 @@ class VMMConnection(object):
This property in available only when running on Xen. This property in available only when running on Xen.
''' '''
# XXX what about the case when we run under KVM, but xen modules are importable? # XXX what about the case when we run under KVM,
# but xen modules are importable?
if 'xen.lowlevel.xs' not in sys.modules: if 'xen.lowlevel.xs' not in sys.modules:
raise AttributeError('xs object is available under Xen hypervisor only') raise AttributeError(
'xs object is available under Xen hypervisor only')
self.init_vmm_connection() self.init_vmm_connection()
return self._xs return self._xs
@ -137,9 +139,11 @@ class VMMConnection(object):
This property in available only when running on Xen. This property in available only when running on Xen.
''' '''
# XXX what about the case when we run under KVM, but xen modules are importable? # XXX what about the case when we run under KVM,
# but xen modules are importable?
if 'xen.lowlevel.xc' not in sys.modules: if 'xen.lowlevel.xc' not in sys.modules:
raise AttributeError('xc object is available under Xen hypervisor only') raise AttributeError(
'xc object is available under Xen hypervisor only')
self.init_vmm_connection() self.init_vmm_connection()
return self._xs return self._xs
@ -148,7 +152,8 @@ class VMMConnection(object):
class QubesHost(object): class QubesHost(object):
'''Basic information about host machine '''Basic information about host machine
:param qubes.Qubes app: Qubes application context (must have :py:attr:`Qubes.vmm` attribute defined) :param qubes.Qubes app: Qubes application context (must have \
:py:attr:`Qubes.vmm` attribute defined)
''' '''
def __init__(self, app): def __init__(self, app):
@ -165,9 +170,11 @@ class QubesHost(object):
self._total_mem = long(memory) * 1024 self._total_mem = long(memory) * 1024
self._no_cpus = cpus self._no_cpus = cpus
self.app.log.debug('QubesHost: no_cpus={} memory_total={}'.format(self.no_cpus, self.memory_total)) self.app.log.debug('QubesHost: no_cpus={} memory_total={}'.format(
self.no_cpus, self.memory_total))
try: try:
self.app.log.debug('QubesHost: xen_free_memory={}'.format(self.get_free_xen_memory())) self.app.log.debug('QubesHost: xen_free_memory={}'.format(
self.get_free_xen_memory()))
except NotImplementedError: except NotImplementedError:
pass pass
@ -275,11 +282,12 @@ class Label(object):
#: label's name like "red" or "green" #: label's name like "red" or "green"
self.name = name self.name = name
#: freedesktop icon name, suitable for use in :py:meth:`PyQt4.QtGui.QIcon.fromTheme` #: freedesktop icon name, suitable for use in
#: :py:meth:`PyQt4.QtGui.QIcon.fromTheme`
self.icon = 'appvm-' + name self.icon = 'appvm-' + name
#: freedesktop icon name, suitable for use in :py:meth:`PyQt4.QtGui.QIcon.fromTheme` #: freedesktop icon name, suitable for use in
#: on DispVMs #: :py:meth:`PyQt4.QtGui.QIcon.fromTheme` on DispVMs
self.icon_dispvm = 'dispvm-' + name self.icon_dispvm = 'dispvm-' + name
@ -402,18 +410,18 @@ class VMCollection(object):
# this violates duck typing, but is needed # this violates duck typing, but is needed
# for VMProperty to function correctly # for VMProperty to function correctly
if not isinstance(value, qubes.vm.BaseVM): if not isinstance(value, qubes.vm.BaseVM):
raise TypeError( raise TypeError('{} holds only BaseVM instances'.format(
'{} holds only BaseVM instances'.format(self.__class__.__name__)) self.__class__.__name__))
if not hasattr(value, 'qid'): if not hasattr(value, 'qid'):
value.qid = self.domains.get_new_unused_qid() value.qid = self.domains.get_new_unused_qid()
if value.qid in self: if value.qid in self:
raise ValueError('This collection already holds VM that has qid={!r} (!r)'.format( raise ValueError('This collection already holds VM that has '
value.qid, self[value.qid])) 'qid={!r} ({!r})'.format(value.qid, self[value.qid]))
if value.name in self: if value.name in self:
raise ValueError('This collection already holds VM that has name={!r} (!r)'.format( raise ValueError('This collection already holds VM that has '
value.name, self[value.name])) 'name={!r} ({!r})'.format(value.name, self[value.name]))
self._dict[value.qid] = value self._dict[value.qid] = value
self.app.fire_event('domain-added', value) self.app.fire_event('domain-added', value)
@ -425,7 +433,7 @@ class VMCollection(object):
if isinstance(key, basestring): if isinstance(key, basestring):
for vm in self: for vm in self:
if (vm.name == key): if vm.name == key:
return vm return vm
raise KeyError(key) raise KeyError(key)
@ -507,13 +515,19 @@ class property(object):
or, when no default, py:class:`exceptions.AttributeError`. or, when no default, py:class:`exceptions.AttributeError`.
:param str name: name of the property :param str name: name of the property
:param collections.Callable setter: if not :py:obj:`None`, this is used to initialise value; first parameter to the function is holder instance and the second is value; this is called before ``type`` :param collections.Callable setter: if not :py:obj:`None`, this is used to \
:param collections.Callable saver: function to coerce value to something readable by setter initialise value; first parameter to the function is holder instance \
and the second is value; this is called before ``type``
:param collections.Callable saver: function to coerce value to something \
readable by setter
:param type type: if not :py:obj:`None`, value is coerced to this type :param type type: if not :py:obj:`None`, value is coerced to this type
:param object default: default value; if callable, will be called with holder as first argument :param object default: default value; if callable, will be called with \
:param int load_stage: stage when property should be loaded (see :py:class:`Qubes` for description of stages) holder as first argument
:param int load_stage: stage when property should be loaded (see \
:py:class:`Qubes` for description of stages)
:param int order: order of evaluation (bigger order values are later) :param int order: order of evaluation (bigger order values are later)
:param str doc: docstring; this should be one paragraph of plain RST, no sphinx-specific features :param str doc: docstring; this should be one paragraph of plain RST, no \
sphinx-specific features
Setters and savers have following signatures: Setters and savers have following signatures:
@ -542,8 +556,9 @@ class property(object):
# internal use only # internal use only
_NO_DEFAULT = object() _NO_DEFAULT = object()
def __init__(self, name, setter=None, saver=None, type=None, default=_NO_DEFAULT, def __init__(self, name, setter=None, saver=None, type=None,
load_stage=2, order=0, save_via_ref=False, doc=None): default=_NO_DEFAULT, load_stage=2, order=0, save_via_ref=False,
doc=None):
self.__name__ = name self.__name__ = name
self._setter = setter self._setter = setter
self._saver = saver if saver is not None else ( self._saver = saver if saver is not None else (
@ -563,8 +578,8 @@ class property(object):
# XXX this violates duck typing, shall we keep it? # XXX this violates duck typing, shall we keep it?
if not isinstance(instance, PropertyHolder): if not isinstance(instance, PropertyHolder):
raise AttributeError( raise AttributeError('qubes.property should be used on '
'qubes.property should be used on qubes.PropertyHolder instances only') 'qubes.PropertyHolder instances only')
try: try:
return getattr(instance, self._attr_name) return getattr(instance, self._attr_name)
@ -618,14 +633,16 @@ class property(object):
has_oldvalue = False has_oldvalue = False
if has_oldvalue: if has_oldvalue:
instance.fire_event_pre('property-pre-deleted:' + self.__name__, oldvalue) instance.fire_event_pre('property-pre-deleted:' + self.__name__,
oldvalue)
else: else:
instance.fire_event_pre('property-pre-deleted:' + self.__name__) instance.fire_event_pre('property-pre-deleted:' + self.__name__)
delattr(instance, self._attr_name) delattr(instance, self._attr_name)
if has_oldvalue: if has_oldvalue:
instance.fire_event('property-deleted:' + self.__name__, oldvalue) instance.fire_event('property-deleted:' + self.__name__,
oldvalue)
else: else:
instance.fire_event('property-deleted:' + self.__name__) instance.fire_event('property-deleted:' + self.__name__)
@ -647,7 +664,8 @@ class property(object):
'''Return parsed documentation string, stripping RST markup. '''Return parsed documentation string, stripping RST markup.
''' '''
if not self.__doc__: return '' if not self.__doc__:
return ''
output, pub = docutils.core.publish_programmatically( output, pub = docutils.core.publish_programmatically(
source_class=docutils.io.StringInput, source_class=docutils.io.StringInput,
@ -692,8 +710,9 @@ class property(object):
:throws AttributeError: always :throws AttributeError: always
''' '''
raise AttributeError('setting {} property on {} instance is forbidden'.format( raise AttributeError(
prop.__name__, self.__class__.__name__)) 'setting {} property on {} instance is forbidden'.format(
prop.__name__, self.__class__.__name__))
@staticmethod @staticmethod
@ -725,7 +744,8 @@ class PropertyHolder(qubes.events.Emitter):
Fired once after all properties are loaded from XML. Individual Fired once after all properties are loaded from XML. Individual
``property-set`` events are not fired. ``property-set`` events are not fired.
.. event:: property-set:<propname> (subject, event, name, newvalue[, oldvalue]) .. event:: property-set:<propname> \
(subject, event, name, newvalue[, oldvalue])
Fired when property changes state. Signature is variable, Fired when property changes state. Signature is variable,
*oldvalue* is present only if there was an old value. *oldvalue* is present only if there was an old value.
@ -734,7 +754,8 @@ class PropertyHolder(qubes.events.Emitter):
:param newvalue: New value of the property :param newvalue: New value of the property
:param oldvalue: Old value of the property :param oldvalue: Old value of the property
.. event:: property-pre-set:<propname> (subject, event, name, newvalue[, oldvalue]) .. event:: property-pre-set:<propname> \
(subject, event, name, newvalue[, oldvalue])
Fired before property changes state. Signature is variable, Fired before property changes state. Signature is variable,
*oldvalue* is present only if there was an old value. *oldvalue* is present only if there was an old value.
@ -743,7 +764,8 @@ class PropertyHolder(qubes.events.Emitter):
:param newvalue: New value of the property :param newvalue: New value of the property
:param oldvalue: Old value of the property :param oldvalue: Old value of the property
.. event:: property-del:<propname> (subject, event, name[, oldvalue]) .. event:: property-del:<propname> \
(subject, event, name[, oldvalue])
Fired when property gets deleted (is set to default). Signature is Fired when property gets deleted (is set to default). Signature is
variable, *oldvalue* is present only if there was an old value. variable, *oldvalue* is present only if there was an old value.
@ -751,7 +773,8 @@ class PropertyHolder(qubes.events.Emitter):
:param name: Property name :param name: Property name
:param oldvalue: Old value of the property :param oldvalue: Old value of the property
.. event:: property-pre-del:<propname> (subject, event, name[, oldvalue]) .. event:: property-pre-del:<propname> \
(subject, event, name[, oldvalue])
Fired before property gets deleted (is set to default). Signature Fired before property gets deleted (is set to default). Signature
is variable, *oldvalue* is present only if there was an old value. is variable, *oldvalue* is present only if there was an old value.
@ -866,7 +889,9 @@ class PropertyHolder(qubes.events.Emitter):
def xml_properties(self, with_defaults=False): def xml_properties(self, with_defaults=False):
'''Iterator that yields XML nodes representing set properties. '''Iterator that yields XML nodes representing set properties.
:param bool with_defaults: If :py:obj:`True`, then it also includes properties which were not set explicite, but have default values filled. :param bool with_defaults: If :py:obj:`True`, then it also includes \
properties which were not set explicite, but have default values \
filled.
''' '''
@ -899,7 +924,8 @@ class PropertyHolder(qubes.events.Emitter):
'''Clone properties from other object. '''Clone properties from other object.
:param PropertyHolder src: source object :param PropertyHolder src: source object
:param list proplist: list of properties (:py:obj:`None` for all properties) :param list proplist: list of properties \
(:py:obj:`None` for all properties)
''' '''
if proplist is None: if proplist is None:
@ -922,8 +948,10 @@ class PropertyHolder(qubes.events.Emitter):
:param prop: property name or object :param prop: property name or object
:type prop: qubes.property or str :type prop: qubes.property or str
:param bool allow_none: if :py:obj:`True`, don't complain if :py:obj:`None` is found :param bool allow_none: if :py:obj:`True`, don't complain if \
:param bool hard: if :py:obj:`True`, raise :py:class:`AssertionError`; if :py:obj:`False`, log warning instead :py:obj:`None` is found
:param bool hard: if :py:obj:`True`, raise :py:class:`AssertionError`; \
if :py:obj:`False`, log warning instead
''' '''
if isinstance(qubes.property, prop): if isinstance(qubes.property, prop):
@ -949,18 +977,23 @@ class VMProperty(property):
:param type vmclass: class that returned VM is supposed to be instance of :param type vmclass: class that returned VM is supposed to be instance of
and all supported by :py:class:`property` with the exception of ``type`` and ``setter`` and all supported by :py:class:`property` with the exception of ``type`` \
and ``setter``
''' '''
def __init__(self, name, vmclass=qubes.vm.BaseVM, allow_none=False, **kwargs): def __init__(self, name, vmclass=qubes.vm.BaseVM, allow_none=False,
**kwargs):
if 'type' in kwargs: if 'type' in kwargs:
raise TypeError("'type' keyword parameter is unsupported in {}".format( raise TypeError(
self.__class__.__name__)) "'type' keyword parameter is unsupported in {}".format(
self.__class__.__name__))
if 'setter' in kwargs: if 'setter' in kwargs:
raise TypeError("'setter' keyword parameter is unsupported in {}".format( raise TypeError(
self.__class__.__name__)) "'setter' keyword parameter is unsupported in {}".format(
self.__class__.__name__))
if not issubclass(vmclass, qubes.vm.BaseVM): if not issubclass(vmclass, qubes.vm.BaseVM):
raise TypeError("'vmclass' should specify a subclass of qubes.vm.BaseVM") raise TypeError(
"'vmclass' should specify a subclass of qubes.vm.BaseVM")
super(VMProperty, self).__init__(name, **kwargs) super(VMProperty, self).__init__(name, **kwargs)
self.vmclass = vmclass self.vmclass = vmclass
@ -981,8 +1014,10 @@ class VMProperty(property):
vm = instance.app.domains[value] vm = instance.app.domains[value]
if not isinstance(vm, self.vmclass): if not isinstance(vm, self.vmclass):
raise TypeError('wrong VM class: domains[{!r}] if of type {!s} and not {!s}'.format( raise TypeError('wrong VM class: domains[{!r}] if of type {!s} '
value, vm.__class__.__name__, self.vmclass.__name__)) 'and not {!s}'.format(value,
vm.__class__.__name__,
self.vmclass.__name__))
super(VMProperty, self).__set__(self, instance, vm) super(VMProperty, self).__set__(self, instance, vm)
@ -1036,10 +1071,12 @@ class Qubes(PropertyHolder):
Methods and attributes: Methods and attributes:
''' '''
default_netvm = VMProperty('default_netvm', load_stage=3, default=None, default_netvm = VMProperty('default_netvm', load_stage=3,
default=None,
doc='''Default NetVM for AppVMs. Initial state is `None`, which means doc='''Default NetVM for AppVMs. Initial state is `None`, which means
that AppVMs are not connected to the Internet.''') that AppVMs are not connected to the Internet.''')
default_fw_netvm = VMProperty('default_fw_netvm', load_stage=3, default=None, default_fw_netvm = VMProperty('default_fw_netvm', load_stage=3,
default=None,
doc='''Default NetVM for ProxyVMs. Initial state is `None`, which means doc='''Default NetVM for ProxyVMs. Initial state is `None`, which means
that ProxyVMs (including FirewallVM) are not connected to the that ProxyVMs (including FirewallVM) are not connected to the
Internet.''') Internet.''')
@ -1274,25 +1311,25 @@ class Qubes(PropertyHolder):
def on_property_pre_set_clockvm(self, event, name, newvalue, oldvalue=None): def on_property_pre_set_clockvm(self, event, name, newvalue, oldvalue=None):
if 'ntpd' in newvalue.services: if 'ntpd' in newvalue.services:
if newvalue.services['ntpd']: if newvalue.services['ntpd']:
raise QubesException( raise QubesException('Cannot set {!r} as {!r} property since '
'Cannot set {!r} as {!r} property since it has ntpd enabled.'.format( 'it has ntpd enabled.'.format(newvalue, name))
newvalue, name))
else: else:
newvalue.services['ntpd'] = False newvalue.services['ntpd'] = False
@qubes.events.handler('property-pre-set:default_netvm') @qubes.events.handler('property-pre-set:default_netvm')
def on_property_pre_set_default_netvm(self, event, name, newvalue, oldvalue=None): def on_property_pre_set_default_netvm(self, event, name, newvalue,
oldvalue=None):
if newvalue is not None and oldvalue is not None \ if newvalue is not None and oldvalue is not None \
and oldvalue.is_running() and not newvalue.is_running() \ and oldvalue.is_running() and not newvalue.is_running() \
and self.domains.get_vms_connected_to(oldvalue): and self.domains.get_vms_connected_to(oldvalue):
raise QubesException( raise QubesException('Cannot change default_netvm to domain that '
'Cannot change default_netvm to domain that is not running ({!r}).'.format( 'is not running ({!r}).'.format(newvalue))
newvalue))
@qubes.events.handler('property-set:default_fw_netvm') @qubes.events.handler('property-set:default_fw_netvm')
def on_property_set_default_netvm(self, event, name, newvalue, oldvalue=None): def on_property_set_default_netvm(self, event, name, newvalue,
oldvalue=None):
for vm in self.domains: for vm in self.domains:
if not vm.provides_network and vm.property_is_default('netvm'): if not vm.provides_network and vm.property_is_default('netvm'):
# fire property-del:netvm as it is responsible for resetting # fire property-del:netvm as it is responsible for resetting
@ -1301,7 +1338,8 @@ class Qubes(PropertyHolder):
@qubes.events.handler('property-set:default_netvm') @qubes.events.handler('property-set:default_netvm')
def on_property_set_default_netvm(self, event, name, newvalue, oldvalue=None): def on_property_set_default_netvm(self, event, name, newvalue,
oldvalue=None):
for vm in self.domains: for vm in self.domains:
if vm.provides_network and vm.property_is_default('netvm'): if vm.provides_network and vm.property_is_default('netvm'):
# fire property-del:netvm as it is responsible for resetting # fire property-del:netvm as it is responsible for resetting

View File

@ -22,7 +22,7 @@
# #
# #
qubes_base_dir = "/var/lib/qubes" qubes_base_dir = "/var/lib/qubes"
system_path = { system_path = {
'qubes_guid_path': '/usr/bin/qubes-guid', 'qubes_guid_path': '/usr/bin/qubes-guid',
'qrexec_daemon_path': '/usr/lib/qubes/qrexec-daemon', 'qrexec_daemon_path': '/usr/lib/qubes/qrexec-daemon',

View File

@ -35,7 +35,8 @@ def fetch_ticket_info(uri):
reader = csv.reader((line + '\n' for line in data.split('\r\n')), reader = csv.reader((line + '\n' for line in data.split('\r\n')),
quoting=csv.QUOTE_MINIMAL, quotechar='"') quoting=csv.QUOTE_MINIMAL, quotechar='"')
return dict(zip(*((cell.decode('utf-8') for cell in row) for row in list(reader)[:2]))) return dict(zip(*((cell.decode('utf-8') for cell in row)
for row in list(reader)[:2])))
def ticket(name, rawtext, text, lineno, inliner, options={}, content=[]): def ticket(name, rawtext, text, lineno, inliner, options={}, content=[]):
@ -45,14 +46,16 @@ def ticket(name, rawtext, text, lineno, inliner, options={}, content=[]):
:param str rawtext: The entire markup snippet, with role :param str rawtext: The entire markup snippet, with role
:param str text: The text marked with the role :param str text: The text marked with the role
:param int lineno: The line number where rawtext appears in the input :param int lineno: The line number where rawtext appears in the input
:param docutils.parsers.rst.states.Inliner inliner: The inliner instance that called this function :param docutils.parsers.rst.states.Inliner inliner: The inliner instance \
that called this function
:param options: Directive options for customisation :param options: Directive options for customisation
:param content: The directive content for customisation :param content: The directive content for customisation
''' '''
ticket = text.lstrip('#') ticket = text.lstrip('#')
if not ticket.isdigit(): if not ticket.isdigit():
msg = inliner.reporter.error('Invalid ticket identificator: {!r}'.format(text), line=lineno) msg = inliner.reporter.error(
'Invalid ticket identificator: {!r}'.format(text), line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg) prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg] return [prb], [msg]
@ -61,7 +64,8 @@ def ticket(name, rawtext, text, lineno, inliner, options={}, content=[]):
try: try:
info = fetch_ticket_info(uri) info = fetch_ticket_info(uri)
except urllib2.HTTPError, e: except urllib2.HTTPError, e:
msg = inliner.reporter.error('Error while fetching ticket info: {!s}'.format(e), line=lineno) msg = inliner.reporter.error(
'Error while fetching ticket info: {!s}'.format(e), line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg) prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg] return [prb], [msg]
@ -76,7 +80,8 @@ def ticket(name, rawtext, text, lineno, inliner, options={}, content=[]):
return [node], [] return [node], []
class versioncheck(docutils.nodes.warning): pass class versioncheck(docutils.nodes.warning):
pass
def visit(self, node): def visit(self, node):
self.visit_admonition(node, 'version') self.visit_admonition(node, 'version')
@ -145,7 +150,8 @@ def parse_event(env, sig, signode):
def setup(app): def setup(app):
app.add_role('ticket', ticket) app.add_role('ticket', ticket)
app.add_config_value('ticket_base_uri', 'https://wiki.qubes-os.org/ticket/', 'env') app.add_config_value('ticket_base_uri',
'https://wiki.qubes-os.org/ticket/', 'env')
app.add_node(versioncheck, app.add_node(versioncheck,
html=(visit, depart), html=(visit, depart),
man=(visit, depart)) man=(visit, depart))

View File

@ -106,7 +106,8 @@ class Emitter(object):
if not hasattr(cls, '__handlers__'): if not hasattr(cls, '__handlers__'):
continue continue
for handler in sorted(cls.__handlers__[event], for handler in sorted(cls.__handlers__[event],
key=(lambda handler: hasattr(handler, 'ha_bound')), reverse=True): key=(lambda handler: hasattr(handler, 'ha_bound')),
reverse=True):
handler(self, event, *args, **kwargs) handler(self, event, *args, **kwargs)
@ -127,7 +128,8 @@ class Emitter(object):
different events. different events.
''' '''
self._fire_event_in_order(reversed(self.__class__.__mro__), event, *args, **kwargs) self._fire_event_in_order(reversed(self.__class__.__mro__), event,
*args, **kwargs)
def fire_event_pre(self, event, *args, **kwargs): def fire_event_pre(self, event, *args, **kwargs):
@ -146,4 +148,5 @@ class Emitter(object):
different events. different events.
''' '''
self._fire_event_in_order(self.__class__.__mro__, event, *args, **kwargs) self._fire_event_in_order(self.__class__.__mro__, event,
*args, **kwargs)

View File

@ -29,7 +29,8 @@ class ExtensionPlugin(qubes.plugins.Plugin):
def __call__(cls, *args, **kwargs): def __call__(cls, *args, **kwargs):
if cls._instance is None: if cls._instance is None:
cls._instance = super(ExtensionPlugin, cls).__call__(*args, **kwargs) cls._instance = super(ExtensionPlugin, cls).__call__(
*args, **kwargs)
return cls._instance return cls._instance
class Extension(object): class Extension(object):
@ -67,7 +68,8 @@ def handler(*events, **kwargs):
:param str event: event type :param str event: event type
:param type vm: VM to hook (leave as None to hook all VMs) :param type vm: VM to hook (leave as None to hook all VMs)
:param bool system: when :py:obj:`True`, hook is system-wide (not attached to any VM) :param bool system: when :py:obj:`True`, hook is system-wide (not attached \
to any VM)
''' '''
def decorator(f): def decorator(f):

View File

@ -12,7 +12,8 @@ import sys
FORMAT_CONSOLE = '%(message)s' FORMAT_CONSOLE = '%(message)s'
FORMAT_LOG = '%(asctime)s %(message)s' FORMAT_LOG = '%(asctime)s %(message)s'
FORMAT_DEBUG = '%(asctime)s [%(processName)s %(module)s.%(funcName)s:%(lineno)d] %(name)s: %(message)s' FORMAT_DEBUG = '%(asctime)s ' \
'[%(processName)s %(module)s.%(funcName)s:%(lineno)d] %(name)s: %(message)s'
LOGPATH = '/var/log/qubes' LOGPATH = '/var/log/qubes'
LOGFILE = os.path.join(LOGPATH, 'qubes.log') LOGFILE = os.path.join(LOGPATH, 'qubes.log')

View File

@ -24,10 +24,12 @@ class Element(object):
def get_description(self, xml=None, wrap=True): def get_description(self, xml=None, wrap=True):
if xml is None: xml = self.xml if xml is None:
xml = self.xml
xml = xml.xpath('./doc:description', namespaces=self.nsmap) xml = xml.xpath('./doc:description', namespaces=self.nsmap)
if not xml: return '' if not xml:
return ''
xml = xml[0] xml = xml[0]
if wrap: if wrap:
@ -38,7 +40,8 @@ class Element(object):
def get_data_type(self, xml=None): def get_data_type(self, xml=None):
if xml is None: xml = self.xml if xml is None:
xml = self.xml
value = xml.xpath('./rng:value', namespaces=self.nsmap) value = xml.xpath('./rng:value', namespaces=self.nsmap)
if value: if value:
@ -75,7 +78,8 @@ class Element(object):
def resolve_ref(self, ref): def resolve_ref(self, ref):
refs = self.xml.xpath('//rng:define[name="{}"]/rng:element'.format(ref['name'])) refs = self.xml.xpath(
'//rng:define[name="{}"]/rng:element'.format(ref['name']))
return refs[0] if refs else None return refs[0] if refs else None
@ -83,7 +87,8 @@ class Element(object):
for xml in self.xml.xpath('''./rng:element | ./rng:ref | for xml in self.xml.xpath('''./rng:element | ./rng:ref |
./rng:optional/rng:element | ./rng:optional/rng:ref | ./rng:optional/rng:element | ./rng:optional/rng:ref |
./rng:zeroOrMore/rng:element | ./rng:zeroOrMore/rng:ref | ./rng:zeroOrMore/rng:element | ./rng:zeroOrMore/rng:ref |
./rng:oneOrMore/rng:element | ./rng:oneOrMore/rng:ref''', namespaces=self.nsmap): ./rng:oneOrMore/rng:element | ./rng:oneOrMore/rng:ref''',
namespaces=self.nsmap):
parent = xml.getparent() parent = xml.getparent()
qname = lxml.etree.QName(parent) qname = lxml.etree.QName(parent)
if parent == self.xml: if parent == self.xml:
@ -99,7 +104,8 @@ class Element(object):
if xml.tag == 'ref': if xml.tag == 'ref':
xml = self.resolve_ref(xml) xml = self.resolve_ref(xml)
if xml is None: continue if xml is None:
continue
yield (self.schema.elements[xml.get('name')], n) yield (self.schema.elements[xml.get('name')], n)
@ -124,7 +130,8 @@ class Element(object):
write_rst_table(stream, attrtable, write_rst_table(stream, attrtable,
('attribute', 'req.', 'type', 'value', 'description')) ('attribute', 'req.', 'type', 'value', 'description'))
childtable = [(':ref:`{0} <qubesxml-element-{0}>`'.format(child.xml.get('name')), n) childtable = [(':ref:`{0} <qubesxml-element-{0}>`'.format(
child.xml.get('name')), n)
for child, n in self.get_child_elements()] for child, n in self.get_child_elements()]
if childtable: if childtable:
stream.write(make_rst_section('Child elements', '^')) stream.write(make_rst_section('Child elements', '^'))
@ -155,8 +162,10 @@ def make_rst_section(heading, c):
def write_rst_table(stream, it, heads): def write_rst_table(stream, it, heads):
stream.write('.. csv-table::\n') stream.write('.. csv-table::\n')
stream.write(' :header: {}\n'.format(', '.join('"{}"'.format(c) for c in heads))) stream.write(' :header: {}\n'.format(', '.join('"{}"'.format(c)
stream.write(' :widths: {}\n\n'.format(', '.join('1' for c in heads))) for c in heads)))
stream.write(' :widths: {}\n\n'.format(', '.join('1'
for c in heads)))
for row in it: for row in it:
stream.write(' {}\n'.format(', '.join('"{}"'.format(i) for i in row))) stream.write(' {}\n'.format(', '.join('"{}"'.format(i) for i in row)))
@ -168,9 +177,14 @@ def main(filename, example):
schema = Schema(lxml.etree.parse(open(filename, 'rb'))) schema = Schema(lxml.etree.parse(open(filename, 'rb')))
sys.stdout.write(make_rst_section('Qubes XML specification', '=')) sys.stdout.write(make_rst_section('Qubes XML specification', '='))
sys.stdout.write('This is the documentation of qubes.xml autogenerated from RelaxNG source.\n\n') sys.stdout.write('''
sys.stdout.write('Quick example, worth thousands lines of specification:\n\n') This is the documentation of qubes.xml autogenerated from RelaxNG source.
sys.stdout.write('.. literalinclude:: {}\n :language: xml\n\n'.format(example))
Quick example, worth thousands lines of specification:
.. literalinclude:: {}
:language: xml
'''[1:].format(example))
for name in sorted(schema.elements): for name in sorted(schema.elements):
schema.elements[name].write_rst(sys.stdout) schema.elements[name].write_rst(sys.stdout)

View File

@ -47,7 +47,8 @@ class XenVMStorage(qubes.storage.VMStorage):
@staticmethod @staticmethod
def _format_disk_dev(path, vdev, script=None, rw=True, type='disk', domain=None): def _format_disk_dev(path, vdev, script=None, rw=True, type='disk',
domain=None):
if path is None: if path is None:
return '' return ''
@ -130,9 +131,9 @@ class XenVMStorage(qubes.storage.VMStorage):
def create_on_disk_root_img(self, source_template=None): def create_on_disk_root_img(self, source_template=None):
if source_template is None: if source_template is None:
f_root = open (self.root_img, "a+b") fd = open(self.root_img, 'a+b')
f_root.truncate (self.root_img_size) fd.truncate(self.root_img_size)
f_root.close () fd.close()
elif self.vm.updateable: elif self.vm.updateable:
# if not updateable, just use template's disk # if not updateable, just use template's disk
@ -142,9 +143,9 @@ class XenVMStorage(qubes.storage.VMStorage):
def resize_private_img(self, size): def resize_private_img(self, size):
f_private = open(self.private_img, "a+b") fd = open(self.private_img, 'a+b')
f_private.truncate(size) fd.truncate(size)
f_private.close() fd.close()
# find loop device if any # find loop device if any
p = subprocess.Popen( p = subprocess.Popen(
@ -166,7 +167,7 @@ class XenVMStorage(qubes.storage.VMStorage):
# TODO: move rootcow_img to this class; the same for vm.is_outdated() # TODO: move rootcow_img to this class; the same for vm.is_outdated()
if os.path.exists(self.vm.rootcow_img): if os.path.exists(self.vm.rootcow_img):
os.rename(self.vm.rootcow_img, self.vm.rootcow_img + '.old') os.rename(self.vm.rootcow_img, self.vm.rootcow_img + '.old')
old_umask = os.umask(002) old_umask = os.umask(002)
f_cow = open(self.vm.rootcow_img, 'w') f_cow = open(self.vm.rootcow_img, 'w')

View File

@ -14,7 +14,8 @@ import qubes.events
#: :py:obj:`True` if running in dom0, :py:obj:`False` otherwise #: :py:obj:`True` if running in dom0, :py:obj:`False` otherwise
in_dom0 = False in_dom0 = False
#: :py:obj:`False` if outside of git repo, path to root of the directory otherwise #: :py:obj:`False` if outside of git repo,
#: path to root of the directory otherwise
in_git = False in_git = False
try: try:
@ -26,7 +27,8 @@ except libvirt.libvirtError:
pass pass
try: try:
in_git = subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).strip() in_git = subprocess.check_output(
['git', 'rev-parse', '--show-toplevel']).strip()
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
# git returned nonzero, we are outside git repo # git returned nonzero, we are outside git repo
pass pass
@ -120,8 +122,10 @@ class QubesTestCase(unittest.TestCase):
:param emitter: emitter which is being checked :param emitter: emitter which is being checked
:type emitter: :py:class:`TestEmitter` :type emitter: :py:class:`TestEmitter`
:param str event: event identifier :param str event: event identifier
:param list args: when given, all items must appear in args passed to event :param list args: when given, all items must appear in args passed to \
:param list kwargs: when given, all items must appear in kwargs passed to event an event
:param list kwargs: when given, all items must appear in kwargs passed \
to an event
''' '''
for ev, ev_args, ev_kwargs in emitter.fired_events: for ev, ev_args, ev_kwargs in emitter.fired_events:
@ -143,8 +147,10 @@ class QubesTestCase(unittest.TestCase):
:param emitter: emitter which is being checked :param emitter: emitter which is being checked
:type emitter: :py:class:`TestEmitter` :type emitter: :py:class:`TestEmitter`
:param str event: event identifier :param str event: event identifier
:param list args: when given, all items must appear in args passed to event :param list args: when given, all items must appear in args passed to \
:param list kwargs: when given, all items must appear in kwargs passed to event an event
:param list kwargs: when given, all items must appear in kwargs passed \
to an event
''' '''
for ev, ev_args, ev_kwargs in emitter.fired_events: for ev, ev_args, ev_kwargs in emitter.fired_events:

View File

@ -82,7 +82,8 @@ class TC_10_property(qubes.tests.QubesTestCase):
def test_023_get_default_func(self): def test_023_get_default_func(self):
class TestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder): class TestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder):
testprop1 = qubes.property('testprop1', default=(lambda self: 'defaultvalue')) testprop1 = qubes.property('testprop1',
default=(lambda self: 'defaultvalue'))
holder = TestHolder(None) holder = TestHolder(None)
self.assertEqual(holder.testprop1, 'defaultvalue') self.assertEqual(holder.testprop1, 'defaultvalue')
@ -202,7 +203,8 @@ class TC_20_PropertyHolder(qubes.tests.QubesTestCase):
expected_prop1.text = 'testvalue1' expected_prop1.text = 'testvalue1'
self.assertXMLEqual(elements_with_defaults[0], expected_prop1) self.assertXMLEqual(elements_with_defaults[0], expected_prop1)
expected_prop2 = lxml.etree.Element('property', name='testprop2', ref='testref2') expected_prop2 = lxml.etree.Element('property',
name='testprop2', ref='testref2')
self.assertXMLEqual(elements_with_defaults[1], expected_prop2) self.assertXMLEqual(elements_with_defaults[1], expected_prop2)
expected_prop3 = lxml.etree.Element('property', name='testprop3') expected_prop3 = lxml.etree.Element('property', name='testprop3')
@ -285,7 +287,8 @@ class TC_30_VMCollection(qubes.tests.QubesTestCase):
self.vms.add(self.testvm1) self.vms.add(self.testvm1)
self.vms.add(self.testvm2) self.vms.add(self.testvm2)
self.assertItemsEqual(self.vms.items(), [(1, self.testvm1), (2, self.testvm2)]) self.assertItemsEqual(self.vms.items(),
[(1, self.testvm1), (2, self.testvm2)])
def test_007_len(self): def test_007_len(self):
self.vms.add(self.testvm1) self.vms.add(self.testvm1)
@ -324,8 +327,10 @@ class TC_30_VMCollection(qubes.tests.QubesTestCase):
class TC_90_Qubes(qubes.tests.QubesTestCase): class TC_90_Qubes(qubes.tests.QubesTestCase):
@qubes.tests.skipUnlessDom0 @qubes.tests.skipUnlessDom0
def test_000_init_empty(self): def test_000_init_empty(self):
try: os.unlink('/tmp/qubestest.xml') try:
except: pass os.unlink('/tmp/qubestest.xml')
except:
pass
app = qubes.Qubes('/tmp/qubestest.xml') app = qubes.Qubes('/tmp/qubestest.xml')
@qubes.tests.skipUnlessGit @qubes.tests.skipUnlessGit

View File

@ -143,8 +143,8 @@ class ANSITestResult(unittest.TestResult):
super(ANSITestResult, self).addUnexpectedSuccess(test) super(ANSITestResult, self).addUnexpectedSuccess(test)
if self.showAll: if self.showAll:
self.stream.writeln( self.stream.writeln(
'{color[yellow]}{color[bold]}unexpected success{color[normal]}'.format( '{color[yellow]}{color[bold]}unexpected success'
color=self.color)) '{color[normal]}'.format(color=self.color))
elif self.dots: elif self.dots:
self.stream.write( self.stream.write(
'{color[yellow]}{color[bold]}u{color[normal]}'.format( '{color[yellow]}{color[bold]}u{color[normal]}'.format(
@ -165,7 +165,7 @@ class ANSITestResult(unittest.TestResult):
def printErrorList(self, flavour, errors): def printErrorList(self, flavour, errors):
for test, err in errors: for test, err in errors:
self.stream.writeln(self.separator1) self.stream.writeln(self.separator1)
self.stream.writeln('%s: %s' % (flavour,self.getDescription(test))) self.stream.writeln('%s: %s' % (flavour, self.getDescription(test)))
self.stream.writeln(self.separator2) self.stream.writeln(self.separator2)
self.stream.writeln('%s' % err) self.stream.writeln('%s' % err)

View File

@ -1,4 +1,28 @@
#!/usr/bin/python2 -O #!/usr/bin/python2 -O
# vim: fileencoding=utf-8
#
# The Qubes OS Project, https://www.qubes-os.org/
#
# Copyright (C) 2010-2015 Joanna Rutkowska <joanna@invisiblethingslab.com>
# Copyright (C) 2011-2015 Marek Marczykowski-Górecki
# <marmarek@invisiblethingslab.com>
# Copyright (C) 2014-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.
#
'''Qubes Virtual Machines '''Qubes Virtual Machines
@ -92,7 +116,8 @@ class DeviceCollection(object):
raise KeyError( raise KeyError(
'device {!r} of class {} already attached to {!r}'.format( 'device {!r} of class {} already attached to {!r}'.format(
device, self._class, self._vm)) device, self._class, self._vm))
self._vm.fire_event_pre('device-pre-attached:{}'.format(self._class), device) self._vm.fire_event_pre('device-pre-attached:{}'.format(self._class),
device)
self._set.add(device) self._set.add(device)
self._vm.fire_event('device-attached:{}'.format(self._class), device) self._vm.fire_event('device-attached:{}'.format(self._class), device)
@ -107,7 +132,8 @@ class DeviceCollection(object):
raise KeyError( raise KeyError(
'device {!r} of class {} not attached to {!r}'.format( 'device {!r} of class {} not attached to {!r}'.format(
device, self._class, self._vm)) device, self._class, self._vm))
self._vm.fire_event_pre('device-pre-detached:{}'.format(self._class), device) self._vm.fire_event_pre('device-pre-detached:{}'.format(self._class),
device)
self._set.remove(device) self._set.remove(device)
self._vm.fire_event('device-detached:{}'.format(self._class), device) self._vm.fire_event('device-detached:{}'.format(self._class), device)
@ -161,7 +187,8 @@ class BaseVM(qubes.PropertyHolder):
self.tags = tags self.tags = tags
self.events_enabled = False self.events_enabled = False
all_names = set(prop.__name__ for prop in self.get_props_list(load_stage=2)) all_names = set(prop.__name__
for prop in self.get_props_list(load_stage=2))
for key in list(kwargs.keys()): for key in list(kwargs.keys()):
if not key in all_names: if not key in all_names:
raise AttributeError( raise AttributeError(
@ -184,8 +211,8 @@ class BaseVM(qubes.PropertyHolder):
vm_cls = QubesVmClasses[vm_type] vm_cls = QubesVmClasses[vm_type]
if 'template' in kwargs: if 'template' in kwargs:
if not vm_cls.is_template_compatible(kwargs['template']): if not vm_cls.is_template_compatible(kwargs['template']):
raise QubesException("Template not compatible with selected " raise QubesException(
"VM type") 'Template not compatible with selected VM type')
vm = vm_cls(qid=qid, collection=self, **kwargs) vm = vm_cls(qid=qid, collection=self, **kwargs)
if not self.verify_new_vm(vm): if not self.verify_new_vm(vm):
@ -219,11 +246,10 @@ class BaseVM(qubes.PropertyHolder):
:param qubes.Qubes app: :py:class:`qubes.Qubes` application instance :param qubes.Qubes app: :py:class:`qubes.Qubes` application instance
:param lxml.etree._Element xml: XML node reference :param lxml.etree._Element xml: XML node reference
:param int load_stage: do not change the default (2) unless you know, what you are doing :param int load_stage: do not change the default (2) unless you know, \
what you are doing
''' '''
# sys.stderr.write('{}.fromxml(app={!r}, xml={!r}, load_stage={})\n'.format(
# cls.__name__, app, xml, load_stage))
if xml is None: if xml is None:
return cls(app) return cls(app)
@ -233,7 +259,8 @@ class BaseVM(qubes.PropertyHolder):
# services # services
for node in xml.xpath('./services/service'): for node in xml.xpath('./services/service'):
services[node.text] = bool(ast.literal_eval(node.get('enabled', 'True'))) services[node.text] = bool(
ast.literal_eval(node.get('enabled', 'True')))
# devices (pci, usb, ...) # devices (pci, usb, ...)
for parent in xml.xpath('./devices'): for parent in xml.xpath('./devices'):
@ -375,7 +402,8 @@ class BaseVM(qubes.PropertyHolder):
args['vcpus'] = str(self.vcpus) args['vcpus'] = str(self.vcpus)
args['mem'] = str(max(self.memory, self.maxmem)) args['mem'] = str(max(self.memory, self.maxmem))
if 'meminfo-writer' in self.services and not self.services['meminfo-writer']: if 'meminfo-writer' in self.services \
and not self.services['meminfo-writer']:
# If dynamic memory management disabled, set maxmem=mem # If dynamic memory management disabled, set maxmem=mem
args['maxmem'] = args['mem'] args['maxmem'] = args['mem']
@ -386,9 +414,10 @@ class BaseVM(qubes.PropertyHolder):
args['dns1'] = self.netvm.gateway args['dns1'] = self.netvm.gateway
args['dns2'] = self.secondary_dns args['dns2'] = self.secondary_dns
args['netmask'] = self.netmask args['netmask'] = self.netmask
args['netdev'] = lxml.etree.tostring(self.lvxml_net_dev(self.ip, self.mac, self.netvm)) args['netdev'] = lxml.etree.tostring(
args['disable_network1'] = ''; self.lvxml_net_dev(self.ip, self.mac, self.netvm))
args['disable_network2'] = ''; args['disable_network1'] = ''
args['disable_network2'] = ''
else: else:
args['ip'] = '' args['ip'] = ''
args['mac'] = '' args['mac'] = ''
@ -397,15 +426,16 @@ class BaseVM(qubes.PropertyHolder):
args['dns2'] = '' args['dns2'] = ''
args['netmask'] = '' args['netmask'] = ''
args['netdev'] = '' args['netdev'] = ''
args['disable_network1'] = '<!--'; args['disable_network1'] = '<!--'
args['disable_network2'] = '-->'; args['disable_network2'] = '-->'
args.update(self.storage.get_config_params()) args.update(self.storage.get_config_params())
if hasattr(self, 'kernelopts'): if hasattr(self, 'kernelopts'):
args['kernelopts'] = self.kernelopts args['kernelopts'] = self.kernelopts
if self.debug: if self.debug:
self.log.info("Debug mode: adding 'earlyprintk=xen' to kernel opts") self.log.info(
"Debug mode: adding 'earlyprintk=xen' to kernel opts")
args['kernelopts'] += ' earlyprintk=xen' args['kernelopts'] += ' earlyprintk=xen'
@ -415,8 +445,10 @@ class BaseVM(qubes.PropertyHolder):
If :py:attr:`qubes.vm.qubesvm.QubesVM.uses_custom_config` is true, this If :py:attr:`qubes.vm.qubesvm.QubesVM.uses_custom_config` is true, this
does nothing. does nothing.
:param str file_path: Path to file to create (default: :py:attr:`qubes.vm.qubesvm.QubesVM.conf_file`) :param str file_path: Path to file to create \
:param bool prepare_dvm: If we are in the process of preparing DisposableVM (default: :py:attr:`qubes.vm.qubesvm.QubesVM.conf_file`)
:param bool prepare_dvm: If we are in the process of preparing \
DisposableVM
''' '''
if file_path is None: if file_path is None:
@ -435,7 +467,8 @@ class BaseVM(qubes.PropertyHolder):
if prepare_dvm: if prepare_dvm:
template_params['name'] = '%NAME%' template_params['name'] = '%NAME%'
template_params['privatedev'] = '' template_params['privatedev'] = ''
template_params['netdev'] = re.sub(r"address='[0-9.]*'", "address='%IP%'", template_params['netdev']) template_params['netdev'] = re.sub(r"address='[0-9.]*'",
"address='%IP%'", template_params['netdev'])
domain_config = conf_template.format(**template_params) domain_config = conf_template.format(**template_params)
# FIXME: This is only for debugging purposes # FIXME: This is only for debugging purposes
@ -470,11 +503,10 @@ class BaseVM(qubes.PropertyHolder):
root = lxml.etree.Element( root = lxml.etree.Element(
"QubesFirewallRules", "QubesFirewallRules",
policy = "allow" if conf["allow"] else "deny", policy=("allow" if conf["allow"] else "deny"),
dns = "allow" if conf["allowDns"] else "deny", dns=("allow" if conf["allowDns"] else "deny"),
icmp = "allow" if conf["allowIcmp"] else "deny", icmp=("allow" if conf["allowIcmp"] else "deny"),
yumProxy = "allow" if conf["allowYumProxy"] else "deny" yumProxy=("allow" if conf["allowYumProxy"] else "deny"))
)
for rule in conf["rules"]: for rule in conf["rules"]:
# For backward compatibility # For backward compatibility
@ -514,7 +546,8 @@ class BaseVM(qubes.PropertyHolder):
os.path.basename(sys.argv[0]), err) os.path.basename(sys.argv[0]), err)
return False return False
# Automatically enable/disable 'yum-proxy-setup' service based on allowYumProxy # Automatically enable/disable 'yum-proxy-setup' service based on
# allowYumProxy
if conf['allowYumProxy']: if conf['allowYumProxy']:
self.services['yum-proxy-setup'] = True self.services['yum-proxy-setup'] = True
else: else:
@ -528,10 +561,15 @@ class BaseVM(qubes.PropertyHolder):
return True return True
def has_firewall(self): def has_firewall(self):
return os.path.exists (self.firewall_conf) return os.path.exists(self.firewall_conf)
def get_firewall_defaults(self): def get_firewall_defaults(self):
return { "rules": list(), "allow": True, "allowDns": True, "allowIcmp": True, "allowYumProxy": False } return {
'rules': list(),
'allow': True,
'allowDns': True,
'allowIcmp': True,
'allowYumProxy': False}
def get_firewall_conf(self): def get_firewall_conf(self):
conf = self.get_firewall_defaults() conf = self.get_firewall_defaults()
@ -582,10 +620,10 @@ class BaseVM(qubes.PropertyHolder):
"%s")): "%s")):
continue continue
else: else:
del(rule["expire"]) del rule["expire"]
del(rule["port"]) del rule["port"]
del(rule["toport"]) del rule["toport"]
conf["rules"].append(rule) conf["rules"].append(rule)

View File

@ -122,7 +122,8 @@ class QubesVM(qubes.vm.BaseVM):
`None`, machine is disconnected. When absent, domain uses default `None`, machine is disconnected. When absent, domain uses default
NetVM.''') NetVM.''')
provides_network = qubes.property('provides_network', type=bool, provides_network = qubes.property('provides_network',
type=bool, setter=qubes.property.bool,
doc='`True` if it is NetVM or ProxyVM, false otherwise.') doc='`True` if it is NetVM or ProxyVM, false otherwise.')
qid = qubes.property('qid', type=int, qid = qubes.property('qid', type=int,
@ -150,20 +151,22 @@ class QubesVM(qubes.vm.BaseVM):
firewall_conf = qubes.property('firewall_conf', type=str, firewall_conf = qubes.property('firewall_conf', type=str,
default='firewall.xml') default='firewall.xml')
installed_by_rpm = qubes.property('installed_by_rpm', type=bool, default=False, installed_by_rpm = qubes.property('installed_by_rpm',
setter=qubes.property.bool, type=bool, setter=qubes.property.bool,
default=False,
doc='''If this domain's image was installed from package tracked by doc='''If this domain's image was installed from package tracked by
package manager.''') package manager.''')
memory = qubes.property('memory', type=int, default=qubes.config.defaults['memory'], memory = qubes.property('memory', type=int,
default=qubes.config.defaults['memory'],
doc='Memory currently available for this VM.') doc='Memory currently available for this VM.')
maxmem = qubes.property('maxmem', type=int, default=None, maxmem = qubes.property('maxmem', type=int, default=None,
doc='''Maximum amount of memory available for this VM (for the purpose doc='''Maximum amount of memory available for this VM (for the purpose
of the memory balancer).''') of the memory balancer).''')
internal = qubes.property('internal', type=bool, default=False, internal = qubes.property('internal', default=False,
setter=qubes.property.bool, type=bool, setter=qubes.property.bool,
doc='''Internal VM (not shown in qubes-manager, don't create appmenus doc='''Internal VM (not shown in qubes-manager, don't create appmenus
entries.''') entries.''')
@ -213,18 +216,18 @@ class QubesVM(qubes.vm.BaseVM):
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.''')
autostart = qubes.property('autostart', type=bool, default=False, autostart = qubes.property('autostart', default=False,
setter=qubes.property.bool, type=bool, setter=qubes.property.bool,
doc='''Setting this to `True` means that VM should be autostarted on dom0 doc='''Setting this to `True` means that VM should be autostarted on
boot.''') dom0 boot.''')
# XXX I don't understand backups # XXX I don't understand backups
include_in_backups = qubes.property('include_in_backups', type=bool, default=True, include_in_backups = qubes.property('include_in_backups', default=True,
setter=qubes.property.bool, type=bool, setter=qubes.property.bool,
doc='If this domain is to be included in default backup.') doc='If this domain is to be included in default backup.')
backup_content = qubes.property('backup_content', type=bool, default=False, backup_content = qubes.property('backup_content', default=False,
setter=qubes.property.bool, type=bool, setter=qubes.property.bool,
doc='FIXME') doc='FIXME')
backup_size = qubes.property('backup_size', type=int, default=0, backup_size = qubes.property('backup_size', type=int, default=0,
@ -235,7 +238,8 @@ class QubesVM(qubes.vm.BaseVM):
# format got changed from %s to str(datetime.datetime) # format got changed from %s to str(datetime.datetime)
backup_timestamp = qubes.property('backup_timestamp', default=None, backup_timestamp = qubes.property('backup_timestamp', default=None,
setter=(lambda self, prop, value: datetime.datetime.fromtimestamp(value)), setter=(lambda self, prop, value:
datetime.datetime.fromtimestamp(value)),
saver=(lambda self, prop, value: value.strftime('%s')), saver=(lambda self, prop, value: value.strftime('%s')),
doc='FIXME') doc='FIXME')
@ -279,12 +283,14 @@ class QubesVM(qubes.vm.BaseVM):
# XXX _update_libvirt_domain? # XXX _update_libvirt_domain?
try: try:
if self.uuid is not None: if self.uuid is not None:
self._libvirt_domain = vmm.libvirt_conn.lookupByUUID(self.uuid.bytes) self._libvirt_domain = vmm.libvirt_conn.lookupByUUID(
self.uuid.bytes)
else: else:
self._libvirt_domain = vmm.libvirt_conn.lookupByName(self.name) self._libvirt_domain = vmm.libvirt_conn.lookupByName(self.name)
self.uuid = uuid.UUID(bytes=self._libvirt_domain.UUID()) self.uuid = uuid.UUID(bytes=self._libvirt_domain.UUID())
except libvirt.libvirtError: except libvirt.libvirtError:
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN: if vmm.libvirt_conn.virConnGetLastError()[0] == \
libvirt.VIR_ERR_NO_DOMAIN:
self._update_libvirt_domain() self._update_libvirt_domain()
else: else:
raise raise
@ -303,7 +309,8 @@ class QubesVM(qubes.vm.BaseVM):
# XXX this should go to to AppVM? # XXX this should go to to AppVM?
@property @property
def private_img(self): def private_img(self):
'''Location of private image of the VM (that contains :file:`/rw` and :file:`/home`).''' '''Location of private image of the VM (that contains :file:`/rw` \
and :file:`/home`).'''
return self.storage.private_img return self.storage.private_img
@ -427,7 +434,7 @@ class QubesVM(qubes.vm.BaseVM):
# Always set if meminfo-writer should be active or not # Always set if meminfo-writer should be active or not
if 'meminfo-writer' not in self.services: if 'meminfo-writer' not in self.services:
self.services['meminfo-writer'] = not (len(self.devices['pci']) > 0) self.services['meminfo-writer'] = not len(self.devices['pci']) > 0
# Additionally force meminfo-writer disabled when VM have PCI devices # Additionally force meminfo-writer disabled when VM have PCI devices
if len(self.devices['pci']) > 0: if len(self.devices['pci']) > 0:
@ -468,13 +475,15 @@ class QubesVM(qubes.vm.BaseVM):
def on_property_del_netvm(self, event, name, old_netvm): def on_property_del_netvm(self, event, name, old_netvm):
# we are changing to default netvm # we are changing to default netvm
new_netvm = self.netvm new_netvm = self.netvm
if new_netvm == old_netvm: return if new_netvm == old_netvm:
return
self.fire_event('property-set:netvm', 'netvm', new_netvm, old_netvm) self.fire_event('property-set:netvm', 'netvm', new_netvm, old_netvm)
@qubes.events.handler('property-set:netvm') @qubes.events.handler('property-set:netvm')
def on_property_set_netvm(self, event, name, new_netvm, old_netvm=None): def on_property_set_netvm(self, event, name, new_netvm, old_netvm=None):
if self.is_running() and new_netvm is not None and not new_netvm.is_running(): if self.is_running() and new_netvm is not None \
and not new_netvm.is_running():
raise QubesException("Cannot dynamically attach to stopped NetVM") raise QubesException("Cannot dynamically attach to stopped NetVM")
if self.netvm is not None: if self.netvm is not None:
@ -490,11 +499,13 @@ class QubesVM(qubes.vm.BaseVM):
if not self._do_not_reset_firewall: if not self._do_not_reset_firewall:
# Set also firewall to block all traffic as discussed in #370 # Set also firewall to block all traffic as discussed in #370
if os.path.exists(self.firewall_conf): if os.path.exists(self.firewall_conf):
shutil.copy(self.firewall_conf, os.path.join(system_path["qubes_base_dir"], shutil.copy(self.firewall_conf,
"backup", "%s-firewall-%s.xml" % (self.name, os.path.join(system_path["qubes_base_dir"],
"backup",
"%s-firewall-%s.xml" % (self.name,
time.strftime('%Y-%m-%d-%H:%M:%S')))) time.strftime('%Y-%m-%d-%H:%M:%S'))))
self.write_firewall_conf({'allow': False, 'allowDns': False, self.write_firewall_conf({'allow': False, 'allowDns': False,
'allowIcmp': False, 'allowYumProxy': False, 'rules': []}) 'allowIcmp': False, 'allowYumProxy': False, 'rules': []})
else: else:
new_netvm.connected_vms.add(self) new_netvm.connected_vms.add(self)
@ -554,13 +565,15 @@ class QubesVM(qubes.vm.BaseVM):
new_conf, old_conf) new_conf, old_conf)
if hasattr(self, 'kernels_dir') and self.kernels_dir is not None: if hasattr(self, 'kernels_dir') and self.kernels_dir is not None:
self.kernels_dir = self.kernels_dir.replace(old_dirpath, new_dirpath) self.kernels_dir = self.kernels_dir.replace(
old_dirpath, new_dirpath)
self._update_libvirt_domain() self._update_libvirt_domain()
@qubes.events.handler('property-pre-set:autostart') @qubes.events.handler('property-pre-set:autostart')
def on_property_pre_set_autostart(self, event, prop, name, value, oldvalue=None): def on_property_pre_set_autostart(self, event, prop, name, value,
oldvalue=None):
if subprocess.call(['sudo', 'systemctl', if subprocess.call(['sudo', 'systemctl',
('enable' if value else 'disable'), ('enable' if value else 'disable'),
'qubes-vm@{}.service'.format(self.name)]): 'qubes-vm@{}.service'.format(self.name)]):
@ -576,8 +589,10 @@ class QubesVM(qubes.vm.BaseVM):
try: try:
# TODO: libvirt-ise # TODO: libvirt-ise
subprocess.check_call(['sudo', system_path["qubes_pciback_cmd"], pci]) subprocess.check_call(
subprocess.check_call(['sudo', 'xl', 'pci-attach', str(self.xid), pci]) ['sudo', system_path["qubes_pciback_cmd"], pci])
subprocess.check_call(
['sudo', 'xl', 'pci-attach', str(self.xid), pci])
except Exception as e: except Exception as e:
print >>sys.stderr, "Failed to attach PCI device on the fly " \ print >>sys.stderr, "Failed to attach PCI device on the fly " \
"(%s), changes will be seen after VM restart" % str(e) "(%s), changes will be seen after VM restart" % str(e)
@ -592,15 +607,17 @@ class QubesVM(qubes.vm.BaseVM):
p = subprocess.Popen(['xl', 'pci-list', str(self.xid)], p = subprocess.Popen(['xl', 'pci-list', str(self.xid)],
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
result = p.communicate() result = p.communicate()
m = re.search(r"^(\d+.\d+)\s+0000:%s$" % pci, result[0], flags=re.MULTILINE) m = re.search(r"^(\d+.\d+)\s+0000:%s$" % pci, result[0],
flags=re.MULTILINE)
if not m: if not m:
print >>sys.stderr, "Device %s already detached" % pci print >>sys.stderr, "Device %s already detached" % pci
return return
vmdev = m.group(1) vmdev = m.group(1)
try: try:
self.run_service("qubes.DetachPciDevice", self.run_service("qubes.DetachPciDevice",
user="root", input="00:%s" % vmdev) user="root", input="00:%s" % vmdev)
subprocess.check_call(['sudo', 'xl', 'pci-detach', str(self.xid), pci]) subprocess.check_call(
['sudo', 'xl', 'pci-detach', str(self.xid), pci])
except Exception as e: except Exception as e:
print >>sys.stderr, "Failed to detach PCI device on the fly " \ print >>sys.stderr, "Failed to detach PCI device on the fly " \
"(%s), changes will be seen after VM restart" % str(e) "(%s), changes will be seen after VM restart" % str(e)
@ -620,7 +637,8 @@ class QubesVM(qubes.vm.BaseVM):
:param int mem_required: FIXME :param int mem_required: FIXME
''' '''
# Intentionally not used is_running(): eliminate also "Paused", "Crashed", "Halting" # Intentionally not used is_running(): eliminate also "Paused",
# "Crashed", "Halting"
if self.get_power_state() != "Halted": if self.get_power_state() != "Halted":
raise QubesException("VM is already running!") raise QubesException("VM is already running!")
@ -631,7 +649,8 @@ class QubesVM(qubes.vm.BaseVM):
if self.netvm is not None: if self.netvm is not None:
if self.netvm.qid != 0: if self.netvm.qid != 0:
if not self.netvm.is_running(): if not self.netvm.is_running():
self.netvm.start(start_guid=start_guid, notify_function=notify_function) self.netvm.start(start_guid=start_guid,
notify_function=notify_function)
self.storage.prepare_for_vm_startup() self.storage.prepare_for_vm_startup()
self._update_libvirt_domain() self._update_libvirt_domain()
@ -643,19 +662,22 @@ class QubesVM(qubes.vm.BaseVM):
try: try:
got_memory = qmemman_client.request_memory(mem_required) got_memory = qmemman_client.request_memory(mem_required)
except IOError as e: except IOError as e:
raise IOError("ERROR: Failed to connect to qmemman: %s" % str(e)) raise IOError('Failed to connect to qmemman: {!s}'.format(e))
if not got_memory: if not got_memory:
qmemman_client.close() qmemman_client.close()
raise MemoryError("ERROR: insufficient memory to start VM '%s'" % self.name) raise MemoryError(
'Insufficient memory to start VM {!r}'.format(self.name))
# Bind pci devices to pciback driver # Bind pci devices to pciback driver
for pci in self.devices['pci']: for pci in self.devices['pci']:
nd = vmm.libvirt_conn.nodeDeviceLookupByName('pci_0000_' + pci.replace(':','_').replace('.','_')) nd = vmm.libvirt_conn.nodeDeviceLookupByName(
'pci_0000_' + pci.replace(':', '_').replace('.', '_'))
try: try:
nd.dettach() nd.dettach()
except libvirt.libvirtError: except libvirt.libvirtError:
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_INTERNAL_ERROR: if vmm.libvirt_conn.virConnGetLastError()[0] == \
# allready detached libvirt.VIR_ERR_INTERNAL_ERROR:
# already detached
pass pass
else: else:
raise raise
@ -675,7 +697,8 @@ class QubesVM(qubes.vm.BaseVM):
if vm.is_proxyvm() and vm.is_running(): if vm.is_proxyvm() and vm.is_running():
vm.write_iptables_xenstore_entry() vm.write_iptables_xenstore_entry()
self.fire_event('domain-started', preparing_dvm=preparing_dvm, start_guid=start_guid) self.fire_event('domain-started',
preparing_dvm=preparing_dvm, start_guid=start_guid)
self.log.warning('Activating the {} VM'.format(self.name)) self.log.warning('Activating the {} VM'.format(self.name))
@ -689,13 +712,15 @@ class QubesVM(qubes.vm.BaseVM):
if qmemman_present: if qmemman_present:
qmemman_client.close() qmemman_client.close()
if self._start_guid_first and start_guid and not preparing_dvm and os.path.exists('/var/run/shm.id'): if self._start_guid_first and start_guid and not preparing_dvm \
and os.path.exists('/var/run/shm.id'):
self.start_guid(notify_function=notify_function) self.start_guid(notify_function=notify_function)
if not preparing_dvm: if not preparing_dvm:
self.start_qrexec_daemon(notify_function=notify_function) self.start_qrexec_daemon(notify_function=notify_function)
if start_guid and not preparing_dvm and os.path.exists('/var/run/shm.id'): if start_guid and not preparing_dvm \
and os.path.exists('/var/run/shm.id'):
self.start_guid(notify_function=notify_function) self.start_guid(notify_function=notify_function)
@ -740,7 +765,8 @@ class QubesVM(qubes.vm.BaseVM):
def pause(self): def pause(self):
'''Pause (suspend) domain. This currently delegates to :py:meth:`suspend`.''' '''Pause (suspend) domain. This currently delegates to \
:py:meth:`suspend`.'''
if not self.is_running(): if not self.is_running():
raise QubesException("VM not running!") raise QubesException("VM not running!")
@ -775,16 +801,22 @@ class QubesVM(qubes.vm.BaseVM):
:param str command: the command to be run :param str command: the command to be run
:param str user: user to run the command as :param str user: user to run the command as
:param bool autostart: if :py:obj:`True`, machine will be started if it is not running :param bool autostart: if :py:obj:`True`, machine will be started if \
it is not running
:param collections.Callable notify_function: FIXME, may go away :param collections.Callable notify_function: FIXME, may go away
:param bool passio: FIXME :param bool passio: FIXME
:param bool passio_popen: if :py:obj:`True`, :py:class:`subprocess.Popen` object has connected ``stdin`` and ``stdout`` :param bool passio_popen: if :py:obj:`True`, \
:param bool passio_stderr: if :py:obj:`True`, :py:class:`subprocess.Popen` has additionaly ``stderr`` connected :py:class:`subprocess.Popen` object has connected ``stdin`` and \
:param bool ignore_stderr: if :py:obj:`True`, ``stderr`` is connected to :file:`/dev/null` ``stdout``
:param bool passio_stderr: if :py:obj:`True`, \
:py:class:`subprocess.Popen` has additionaly ``stderr`` connected
:param bool ignore_stderr: if :py:obj:`True`, ``stderr`` is connected \
to :file:`/dev/null`
:param str localcmd: local command to communicate with remote command :param str localcmd: local command to communicate with remote command
:param bool wait: if :py:obj:`True`, wait for command completion :param bool wait: if :py:obj:`True`, wait for command completion
:param bool gui: when autostarting, also start gui daemon :param bool gui: when autostarting, also start gui daemon
:param bool filter_esc: filter escape sequences to protect terminal emulator :param bool filter_esc: filter escape sequences to protect terminal \
emulator
''' '''
if user is None: if user is None:
@ -796,15 +828,18 @@ class QubesVM(qubes.vm.BaseVM):
try: try:
if notify_function is not None: if notify_function is not None:
notify_function("info", "Starting the '{0}' VM...".format(self.name)) notify_function('info',
'Starting the {!r} VM...'.format(self.name))
self.start(start_guid=gui, notify_function=notify_function) self.start(start_guid=gui, notify_function=notify_function)
except (IOError, OSError, QubesException) as err: except (IOError, OSError, QubesException) as e:
raise QubesException("Error while starting the '{0}' VM: {1}".format(self.name, err)) raise QubesException(
'Error while starting the {!r} VM: {!s}'.format(
self.name, e))
except (MemoryError) as err: except (MemoryError) as err:
raise QubesException("Not enough memory to start '{0}' VM! " raise QubesException('Not enough memory to start {!r} VM! '
"Close one or more running VMs and try " 'Close one or more running VMs and try again.'.format(
"again.".format(self.name)) self.name))
if self.is_paused(): if self.is_paused():
raise QubesException("VM is paused") raise QubesException("VM is paused")
@ -812,22 +847,26 @@ class QubesVM(qubes.vm.BaseVM):
raise QubesException( raise QubesException(
"Domain '{}': qrexec not connected.".format(self.name)) "Domain '{}': qrexec not connected.".format(self.name))
if gui and os.getenv("DISPLAY") is not None and not self.is_guid_running(): if gui and os.getenv("DISPLAY") is not None \
self.start_guid(verbose = verbose, notify_function = notify_function) and not self.is_guid_running():
self.start_guid(verbose=verbose, notify_function=notify_function)
args = [system_path["qrexec_client_path"], "-d", str(self.name), "%s:%s" % (user, command)] args = [system_path["qrexec_client_path"],
"-d", str(self.name),
'{}:{}'.format(user, command)]
if localcmd is not None: if localcmd is not None:
args += [ "-l", localcmd] args += ['-l', localcmd]
if filter_esc: if filter_esc:
args += ["-t"] args += ['-t']
if os.isatty(sys.stderr.fileno()): if os.isatty(sys.stderr.fileno()):
args += ["-T"] args += ['-T']
# TODO: QSB#13 # TODO: QSB#13
if passio: if passio:
if os.name == 'nt': if os.name == 'nt':
# wait for qrexec-client to exit, otherwise client is not properly attached to console # wait for qrexec-client to exit, otherwise client is not
# if qvm-run is executed from cmd.exe # properly attached to console if qvm-run is executed from
# cmd.exe
ret = subprocess.call(args) ret = subprocess.call(args)
exit(ret) exit(ret)
os.execv(system_path["qrexec_client_path"], args) os.execv(system_path["qrexec_client_path"], args)
@ -839,7 +878,7 @@ class QubesVM(qubes.vm.BaseVM):
call_kwargs['stderr'] = null call_kwargs['stderr'] = null
if passio_popen: if passio_popen:
popen_kwargs={'stdout': subprocess.PIPE} popen_kwargs = {'stdout': subprocess.PIPE}
popen_kwargs['stdin'] = subprocess.PIPE popen_kwargs['stdin'] = subprocess.PIPE
if passio_stderr: if passio_stderr:
popen_kwargs['stderr'] = subprocess.PIPE popen_kwargs['stderr'] = subprocess.PIPE
@ -910,13 +949,14 @@ class QubesVM(qubes.vm.BaseVM):
guid_cmd += ['-q'] guid_cmd += ['-q']
retcode = subprocess.call(guid_cmd) retcode = subprocess.call(guid_cmd)
if (retcode != 0) : if retcode != 0:
raise QubesException("Cannot start qubes-guid!") raise QubesException('Cannot start qubes-guid!')
self.log.info('Sending monitor layout') self.log.info('Sending monitor layout')
try: try:
subprocess.call([system_path["monitor_layout_notify_cmd"], self.name]) subprocess.call(
[system_path['monitor_layout_notify_cmd'], self.name])
except Exception as e: except Exception as e:
self.log.error('ERROR: {!s}'.format(e)) self.log.error('ERROR: {!s}'.format(e))
@ -937,8 +977,8 @@ class QubesVM(qubes.vm.BaseVM):
qrexec_env['QREXEC_STARTUP_TIMEOUT'] = str(self.qrexec_timeout) qrexec_env['QREXEC_STARTUP_TIMEOUT'] = str(self.qrexec_timeout)
retcode = subprocess.call([system_path["qrexec_daemon_path"]] + retcode = subprocess.call([system_path["qrexec_daemon_path"]] +
qrexec_args, env=qrexec_env) qrexec_args, env=qrexec_env)
if (retcode != 0) : if retcode != 0:
raise OSError("Cannot execute qrexec-daemon!") raise OSError('Cannot execute qrexec-daemon!')
def start_qubesdb(self): def start_qubesdb(self):
@ -998,7 +1038,8 @@ class QubesVM(qubes.vm.BaseVM):
os.path.join(self.dir_path, os.path.join(self.dir_path,
qubes.config.vm_files["kernels_subdir"], f)) qubes.config.vm_files["kernels_subdir"], f))
self.log.info('Creating icon symlink: {0} -> {1}'.format(self.icon_path, self.label.icon_path)) self.log.info('Creating icon symlink: {} -> {}'.format(
self.icon_path, self.label.icon_path))
if hasattr(os, "symlink"): if hasattr(os, "symlink"):
os.symlink(self.label.icon_path, self.icon_path) os.symlink(self.label.icon_path, self.icon_path)
else: else:
@ -1021,8 +1062,12 @@ class QubesVM(qubes.vm.BaseVM):
# FIXME move this to qubes.storage.xen.XenVMStorage # FIXME move this to qubes.storage.xen.XenVMStorage
retcode = 0 retcode = 0
if self.is_running(): if self.is_running():
retcode = self.run("while [ \"`blockdev --getsize64 /dev/xvdb`\" -lt {0} ]; do ".format(size) + retcode = self.run('''
"head /dev/xvdb > /dev/null; sleep 0.2; done; resize2fs /dev/xvdb", user="root", wait=True) while [ "`blockdev --getsize64 /dev/xvdb`" -lt {0} ]; do
head /dev/xvdb >/dev/null;
sleep 0.2;
done;
resize2fs /dev/xvdb'''.format(size), user="root", wait=True)
if retcode != 0: if retcode != 0:
raise QubesException("resize2fs failed") raise QubesException("resize2fs failed")
@ -1123,7 +1168,8 @@ class QubesVM(qubes.vm.BaseVM):
is undefined). is undefined).
=============== ======================================================== =============== ========================================================
``Paused`` state is currently unavailable because of missing code in libvirt/xen glue. ``Paused`` state is currently unavailable because of missing code in
libvirt/xen glue.
FIXME: graph below may be incomplete and wrong. Click on method name to FIXME: graph below may be incomplete and wrong. Click on method name to
see its documentation. see its documentation.
@ -1195,9 +1241,12 @@ class QubesVM(qubes.vm.BaseVM):
{ rank=sink; Paused Suspended }; { rank=sink; Paused Suspended };
} }
.. seealso:: http://wiki.libvirt.org/page/VM_lifecycle .. seealso::
http://wiki.libvirt.org/page/VM_lifecycle
Description of VM life cycle from the point of view of libvirt.
.. seealso:: https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainState https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainState
Libvirt API for changing state of a domain.
''' '''
@ -1230,7 +1279,8 @@ class QubesVM(qubes.vm.BaseVM):
def is_running(self): def is_running(self):
'''Check whether this domain is running. '''Check whether this domain is running.
:returns: :py:obj:`True` if this domain is started, :py:obj:`False` otherwise. :returns: :py:obj:`True` if this domain is started, \
:py:obj:`False` otherwise.
:rtype: bool :rtype: bool
''' '''
@ -1240,7 +1290,8 @@ class QubesVM(qubes.vm.BaseVM):
def is_paused(self): def is_paused(self):
'''Check whether this domain is paused. '''Check whether this domain is paused.
:returns: :py:obj:`True` if this domain is paused, :py:obj:`False` otherwise. :returns: :py:obj:`True` if this domain is paused, \
:py:obj:`False` otherwise.
:rtype: bool :rtype: bool
''' '''
@ -1251,7 +1302,8 @@ class QubesVM(qubes.vm.BaseVM):
def is_guid_running(self): def is_guid_running(self):
'''Check whether gui daemon for this domain is available. '''Check whether gui daemon for this domain is available.
:returns: :py:obj:`True` if guid is running, :py:obj:`False` otherwise. :returns: :py:obj:`True` if guid is running, \
:py:obj:`False` otherwise.
:rtype: bool :rtype: bool
''' '''
xid = self.xid xid = self.xid
@ -1265,7 +1317,8 @@ class QubesVM(qubes.vm.BaseVM):
def is_qrexec_running(self): def is_qrexec_running(self):
'''Check whether qrexec for this domain is available. '''Check whether qrexec for this domain is available.
:returns: :py:obj:`True` if qrexec is running, :py:obj:`False` otherwise. :returns: :py:obj:`True` if qrexec is running, \
:py:obj:`False` otherwise.
:rtype: bool :rtype: bool
''' '''
if self.xid < 0: if self.xid < 0:
@ -1278,7 +1331,8 @@ class QubesVM(qubes.vm.BaseVM):
Currently this checks for running guid and qrexec. Currently this checks for running guid and qrexec.
:returns: :py:obj:`True` if qrexec is running, :py:obj:`False` otherwise. :returns: :py:obj:`True` if qrexec is running, \
:py:obj:`False` otherwise.
:rtype: bool :rtype: bool
''' '''
@ -1402,7 +1456,8 @@ class QubesVM(qubes.vm.BaseVM):
def get_disk_utilization(self): def get_disk_utilization(self):
'''Return total space actually occuppied by all files belonging to this domain. '''Return total space actually occuppied by all files belonging to \
this domain.
:returns: domain's total disk usage [FIXME unit] :returns: domain's total disk usage [FIXME unit]
:rtype: FIXME :rtype: FIXME
@ -1445,7 +1500,8 @@ class QubesVM(qubes.vm.BaseVM):
return None return None
# TODO shouldn't this be qubesdb? # TODO shouldn't this be qubesdb?
start_time = self.app.vmm.xs.read('', "/vm/%s/start_time" % str(self.uuid)) start_time = self.app.vmm.xs.read('',
'/vm/{}/start_time'.format(self.uuid))
if start_time != '': if start_time != '':
return datetime.datetime.fromtimestamp(float(start_time)) return datetime.datetime.fromtimestamp(float(start_time))
else: else:
@ -1454,7 +1510,8 @@ class QubesVM(qubes.vm.BaseVM):
# XXX this probably should go to AppVM # XXX this probably should go to AppVM
def is_outdated(self): def is_outdated(self):
'''Check whether domain needs restart to update root image from template. '''Check whether domain needs restart to update root image from \
template.
:returns: :py:obj:`True` if is outdated, :py:obj:`False` otherwise. :returns: :py:obj:`True` if is outdated, :py:obj:`False` otherwise.
:rtype: bool :rtype: bool
@ -1485,7 +1542,8 @@ class QubesVM(qubes.vm.BaseVM):
# FIXME # FIXME
# 51712 (0xCA00) is xvda # 51712 (0xCA00) is xvda
# backend node name not available through xenapi :( # backend node name not available through xenapi :(
used_dmdev = vmm.xs.read('', "/local/domain/0/backend/vbd/{0}/51712/node".format(self.xid)) used_dmdev = vmm.xs.read('',
'/local/domain/0/backend/vbd/{}/51712/node'.format(self.xid))
return used_dmdev != current_dmdev return used_dmdev != current_dmdev
@ -1493,7 +1551,8 @@ class QubesVM(qubes.vm.BaseVM):
def is_networked(self): def is_networked(self):
'''Check whether this VM can reach network (firewall notwithstanding). '''Check whether this VM can reach network (firewall notwithstanding).
:returns: :py:obj:`True` if is machine can reach network, :py:obj:`False` otherwise. :returns: :py:obj:`True` if is machine can reach network, \
:py:obj:`False` otherwise.
:rtype: bool :rtype: bool
''' '''
@ -1539,7 +1598,7 @@ class QubesVM(qubes.vm.BaseVM):
tzname = qubes.utils.get_timezone() tzname = qubes.utils.get_timezone()
if tzname: if tzname:
self.qdb.write("/qubes-timezone", tzname) self.qdb.write("/qubes-timezone", tzname)
for srv in self.services.keys(): for srv in self.services.keys():
# convert True/False to "1"/"0" # convert True/False to "1"/"0"
@ -1555,8 +1614,9 @@ class QubesVM(qubes.vm.BaseVM):
# TODO: Currently the whole qmemman is quite Xen-specific, so stay with # TODO: Currently the whole qmemman is quite Xen-specific, so stay with
# xenstore for it until decided otherwise # xenstore for it until decided otherwise
if qmemman_present: if qmemman_present:
vmm.xs.set_permissions('', '/local/domain/{0}/memory'.format(self.xid), vmm.xs.set_permissions('',
[{ 'dom': self.xid }]) '/local/domain/{}/memory'.format(self.xid),
[{'dom': self.xid}])
self.fire_event('qdb-created') self.fire_event('qdb-created')
@ -1570,7 +1630,8 @@ class QubesVM(qubes.vm.BaseVM):
self._libvirt_domain = vmm.libvirt_conn.defineXML(domain_config) self._libvirt_domain = vmm.libvirt_conn.defineXML(domain_config)
self.uuid = uuid.UUID(bytes=self._libvirt_domain.UUID()) self.uuid = uuid.UUID(bytes=self._libvirt_domain.UUID())
except libvirt.libvirtError: except libvirt.libvirtError:
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN: if vmm.libvirt_conn.virConnGetLastError()[0] == \
libvirt.VIR_ERR_NO_DOMAIN:
# accept the fact that libvirt doesn't know anything about this # accept the fact that libvirt doesn't know anything about this
# domain... # domain...
pass pass
@ -1592,13 +1653,15 @@ class QubesVM(qubes.vm.BaseVM):
dev_basepath = '/local/domain/%d/device/vif' % self.xid dev_basepath = '/local/domain/%d/device/vif' % self.xid
for dev in self.app.vmm.xs.ls('', dev_basepath): for dev in self.app.vmm.xs.ls('', dev_basepath):
# check if backend domain is alive # check if backend domain is alive
backend_xid = int(self.app.vmm.xs.read('', '%s/%s/backend-id' % (dev_basepath, dev))) backend_xid = int(self.app.vmm.xs.read('',
'{}/{}/backend-id'.format(dev_basepath, dev)))
if backend_xid in self.app.vmm.libvirt_conn.listDomainsID(): if backend_xid in self.app.vmm.libvirt_conn.listDomainsID():
# check if device is still active # check if device is still active
if self.app.vmm.xs.read('', '%s/%s/state' % (dev_basepath, dev)) == '4': if self.app.vmm.xs.read('',
'{}/{}/state'.format(dev_basepath, dev)) == '4':
continue continue
# remove dead device # remove dead device
self.app.vmm.xs.rm('', '%s/%s' % (dev_basepath, dev)) self.app.vmm.xs.rm('', '{}/{}'.format(dev_basepath, dev))
@ -1635,17 +1698,13 @@ class QubesVM(qubes.vm.BaseVM):
# attrs = { # attrs = {
#
##### Internal attributes - will be overriden in __init__ regardless of args
# those should be __builtin__.property of something
# used to suppress side effects of clone_attrs
# XXX probably will be obsoleted by .events_enabled # XXX probably will be obsoleted by .events_enabled
# "_do_not_reset_firewall": { "func": lambda x: False }, # "_do_not_reset_firewall": { "func": lambda x: False },
# XXX WTF? # XXX WTF?
# "kernels_dir": { # "kernels_dir": {
# # for backward compatibility (or another rare case): kernel=None -> kernel in VM dir # # for backward compatibility (or another rare case): kernel=None ->
# # kernel in VM dir
# "func": lambda x: \ # "func": lambda x: \
# os.path.join(system_path["qubes_kernels_base_dir"], # os.path.join(system_path["qubes_kernels_base_dir"],
# self.kernel) if self.kernel is not None \ # self.kernel) if self.kernel is not None \