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:
@ -166,7 +172,7 @@ class QubesTestCase(unittest.TestCase):
Schema can be given in a couple of ways: Schema can be given in a couple of ways:
- As separate file. This is most common, and also the only way to - As separate file. This is most common, and also the only way to
handle file inclusion. Call with file name as second argument. handle file inclusion. Call with file name as second argument.
- As string containing actual schema. Put that string in *schema* - As string containing actual schema. Put that string in *schema*
keyword argument. keyword argument.

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

@ -76,5 +76,5 @@ class TC_00_setters(qubes.tests.QubesTestCase):
class TC_90_QubesVM(qubes.tests.QubesTestCase): class TC_90_QubesVM(qubes.tests.QubesTestCase):
@unittest.skip('test not implemented') @unittest.skip('test not implemented')
def test_000_init(self): def test_000_init(self):
pass pass

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 \