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.
'''
# 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:
raise AttributeError('xs object is available under Xen hypervisor only')
raise AttributeError(
'xs object is available under Xen hypervisor only')
self.init_vmm_connection()
return self._xs
@ -137,9 +139,11 @@ class VMMConnection(object):
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:
raise AttributeError('xc object is available under Xen hypervisor only')
raise AttributeError(
'xc object is available under Xen hypervisor only')
self.init_vmm_connection()
return self._xs
@ -148,7 +152,8 @@ class VMMConnection(object):
class QubesHost(object):
'''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):
@ -165,9 +170,11 @@ class QubesHost(object):
self._total_mem = long(memory) * 1024
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:
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:
pass
@ -275,11 +282,12 @@ class Label(object):
#: label's name like "red" or "green"
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
#: freedesktop icon name, suitable for use in :py:meth:`PyQt4.QtGui.QIcon.fromTheme`
#: on DispVMs
#: freedesktop icon name, suitable for use in
#: :py:meth:`PyQt4.QtGui.QIcon.fromTheme` on DispVMs
self.icon_dispvm = 'dispvm-' + name
@ -402,18 +410,18 @@ class VMCollection(object):
# this violates duck typing, but is needed
# for VMProperty to function correctly
if not isinstance(value, qubes.vm.BaseVM):
raise TypeError(
'{} holds only BaseVM instances'.format(self.__class__.__name__))
raise TypeError('{} holds only BaseVM instances'.format(
self.__class__.__name__))
if not hasattr(value, 'qid'):
value.qid = self.domains.get_new_unused_qid()
if value.qid in self:
raise ValueError('This collection already holds VM that has qid={!r} (!r)'.format(
value.qid, self[value.qid]))
raise ValueError('This collection already holds VM that has '
'qid={!r} ({!r})'.format(value.qid, self[value.qid]))
if value.name in self:
raise ValueError('This collection already holds VM that has name={!r} (!r)'.format(
value.name, self[value.name]))
raise ValueError('This collection already holds VM that has '
'name={!r} ({!r})'.format(value.name, self[value.name]))
self._dict[value.qid] = value
self.app.fire_event('domain-added', value)
@ -425,7 +433,7 @@ class VMCollection(object):
if isinstance(key, basestring):
for vm in self:
if (vm.name == key):
if vm.name == key:
return vm
raise KeyError(key)
@ -507,13 +515,19 @@ class property(object):
or, when no default, py:class:`exceptions.AttributeError`.
: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 saver: function to coerce value to something readable by setter
: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 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 object default: default value; if callable, will be called with holder as first argument
:param int load_stage: stage when property should be loaded (see :py:class:`Qubes` for description of stages)
:param object default: default value; if callable, will be called with \
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 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:
@ -542,8 +556,9 @@ class property(object):
# internal use only
_NO_DEFAULT = object()
def __init__(self, name, setter=None, saver=None, type=None, default=_NO_DEFAULT,
load_stage=2, order=0, save_via_ref=False, doc=None):
def __init__(self, name, setter=None, saver=None, type=None,
default=_NO_DEFAULT, load_stage=2, order=0, save_via_ref=False,
doc=None):
self.__name__ = name
self._setter = setter
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?
if not isinstance(instance, PropertyHolder):
raise AttributeError(
'qubes.property should be used on qubes.PropertyHolder instances only')
raise AttributeError('qubes.property should be used on '
'qubes.PropertyHolder instances only')
try:
return getattr(instance, self._attr_name)
@ -618,14 +633,16 @@ class property(object):
has_oldvalue = False
if has_oldvalue:
instance.fire_event_pre('property-pre-deleted:' + self.__name__, oldvalue)
instance.fire_event_pre('property-pre-deleted:' + self.__name__,
oldvalue)
else:
instance.fire_event_pre('property-pre-deleted:' + self.__name__)
delattr(instance, self._attr_name)
if has_oldvalue:
instance.fire_event('property-deleted:' + self.__name__, oldvalue)
instance.fire_event('property-deleted:' + self.__name__,
oldvalue)
else:
instance.fire_event('property-deleted:' + self.__name__)
@ -647,7 +664,8 @@ class property(object):
'''Return parsed documentation string, stripping RST markup.
'''
if not self.__doc__: return ''
if not self.__doc__:
return ''
output, pub = docutils.core.publish_programmatically(
source_class=docutils.io.StringInput,
@ -692,7 +710,8 @@ class property(object):
:throws AttributeError: always
'''
raise AttributeError('setting {} property on {} instance is forbidden'.format(
raise AttributeError(
'setting {} property on {} instance is forbidden'.format(
prop.__name__, self.__class__.__name__))
@ -725,7 +744,8 @@ class PropertyHolder(qubes.events.Emitter):
Fired once after all properties are loaded from XML. Individual
``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,
*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 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,
*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 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
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 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
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):
'''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.
: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:
@ -922,8 +948,10 @@ class PropertyHolder(qubes.events.Emitter):
:param prop: property name or object
: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 hard: if :py:obj:`True`, raise :py:class:`AssertionError`; if :py:obj:`False`, log warning instead
:param bool allow_none: if :py:obj:`True`, don't complain if \
: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):
@ -949,18 +977,23 @@ class VMProperty(property):
: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:
raise TypeError("'type' keyword parameter is unsupported in {}".format(
raise TypeError(
"'type' keyword parameter is unsupported in {}".format(
self.__class__.__name__))
if 'setter' in kwargs:
raise TypeError("'setter' keyword parameter is unsupported in {}".format(
raise TypeError(
"'setter' keyword parameter is unsupported in {}".format(
self.__class__.__name__))
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)
self.vmclass = vmclass
@ -981,8 +1014,10 @@ class VMProperty(property):
vm = instance.app.domains[value]
if not isinstance(vm, self.vmclass):
raise TypeError('wrong VM class: domains[{!r}] if of type {!s} and not {!s}'.format(
value, vm.__class__.__name__, self.vmclass.__name__))
raise TypeError('wrong VM class: domains[{!r}] if of type {!s} '
'and not {!s}'.format(value,
vm.__class__.__name__,
self.vmclass.__name__))
super(VMProperty, self).__set__(self, instance, vm)
@ -1036,10 +1071,12 @@ class Qubes(PropertyHolder):
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
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
that ProxyVMs (including FirewallVM) are not connected to the
Internet.''')
@ -1274,25 +1311,25 @@ class Qubes(PropertyHolder):
def on_property_pre_set_clockvm(self, event, name, newvalue, oldvalue=None):
if 'ntpd' in newvalue.services:
if newvalue.services['ntpd']:
raise QubesException(
'Cannot set {!r} as {!r} property since it has ntpd enabled.'.format(
newvalue, name))
raise QubesException('Cannot set {!r} as {!r} property since '
'it has ntpd enabled.'.format(newvalue, name))
else:
newvalue.services['ntpd'] = False
@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 \
and oldvalue.is_running() and not newvalue.is_running() \
and self.domains.get_vms_connected_to(oldvalue):
raise QubesException(
'Cannot change default_netvm to domain that is not running ({!r}).'.format(
newvalue))
raise QubesException('Cannot change default_netvm to domain that '
'is not running ({!r}).'.format(newvalue))
@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:
if not vm.provides_network and vm.property_is_default('netvm'):
# fire property-del:netvm as it is responsible for resetting
@ -1301,7 +1338,8 @@ class Qubes(PropertyHolder):
@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:
if vm.provides_network and vm.property_is_default('netvm'):
# fire property-del:netvm as it is responsible for resetting

View File

@ -35,7 +35,8 @@ def fetch_ticket_info(uri):
reader = csv.reader((line + '\n' for line in data.split('\r\n')),
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=[]):
@ -45,14 +46,16 @@ def ticket(name, rawtext, text, lineno, inliner, options={}, content=[]):
:param str rawtext: The entire markup snippet, with role
:param str text: The text marked with the role
: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 content: The directive content for customisation
'''
ticket = text.lstrip('#')
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)
return [prb], [msg]
@ -61,7 +64,8 @@ def ticket(name, rawtext, text, lineno, inliner, options={}, content=[]):
try:
info = fetch_ticket_info(uri)
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)
return [prb], [msg]
@ -76,7 +80,8 @@ def ticket(name, rawtext, text, lineno, inliner, options={}, content=[]):
return [node], []
class versioncheck(docutils.nodes.warning): pass
class versioncheck(docutils.nodes.warning):
pass
def visit(self, node):
self.visit_admonition(node, 'version')
@ -145,7 +150,8 @@ def parse_event(env, sig, signode):
def setup(app):
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,
html=(visit, depart),
man=(visit, depart))

View File

@ -106,7 +106,8 @@ class Emitter(object):
if not hasattr(cls, '__handlers__'):
continue
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)
@ -127,7 +128,8 @@ class Emitter(object):
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):
@ -146,4 +148,5 @@ class Emitter(object):
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):
if cls._instance is None:
cls._instance = super(ExtensionPlugin, cls).__call__(*args, **kwargs)
cls._instance = super(ExtensionPlugin, cls).__call__(
*args, **kwargs)
return cls._instance
class Extension(object):
@ -67,7 +68,8 @@ def handler(*events, **kwargs):
:param str event: event type
: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):

View File

@ -12,7 +12,8 @@ import sys
FORMAT_CONSOLE = '%(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'
LOGFILE = os.path.join(LOGPATH, 'qubes.log')

View File

@ -24,10 +24,12 @@ class Element(object):
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)
if not xml: return ''
if not xml:
return ''
xml = xml[0]
if wrap:
@ -38,7 +40,8 @@ class Element(object):
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)
if value:
@ -75,7 +78,8 @@ class Element(object):
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
@ -83,7 +87,8 @@ class Element(object):
for xml in self.xml.xpath('''./rng:element | ./rng:ref |
./rng:optional/rng:element | ./rng:optional/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()
qname = lxml.etree.QName(parent)
if parent == self.xml:
@ -99,7 +104,8 @@ class Element(object):
if xml.tag == 'ref':
xml = self.resolve_ref(xml)
if xml is None: continue
if xml is None:
continue
yield (self.schema.elements[xml.get('name')], n)
@ -124,7 +130,8 @@ class Element(object):
write_rst_table(stream, attrtable,
('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()]
if childtable:
stream.write(make_rst_section('Child elements', '^'))
@ -155,8 +162,10 @@ def make_rst_section(heading, c):
def write_rst_table(stream, it, heads):
stream.write('.. csv-table::\n')
stream.write(' :header: {}\n'.format(', '.join('"{}"'.format(c) for c in heads)))
stream.write(' :widths: {}\n\n'.format(', '.join('1' for c in heads)))
stream.write(' :header: {}\n'.format(', '.join('"{}"'.format(c)
for c in heads)))
stream.write(' :widths: {}\n\n'.format(', '.join('1'
for c in heads)))
for row in it:
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')))
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('Quick example, worth thousands lines of specification:\n\n')
sys.stdout.write('.. literalinclude:: {}\n :language: xml\n\n'.format(example))
sys.stdout.write('''
This is the documentation of qubes.xml autogenerated from RelaxNG source.
Quick example, worth thousands lines of specification:
.. literalinclude:: {}
:language: xml
'''[1:].format(example))
for name in sorted(schema.elements):
schema.elements[name].write_rst(sys.stdout)

View File

@ -47,7 +47,8 @@ class XenVMStorage(qubes.storage.VMStorage):
@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:
return ''
@ -130,9 +131,9 @@ class XenVMStorage(qubes.storage.VMStorage):
def create_on_disk_root_img(self, source_template=None):
if source_template is None:
f_root = open (self.root_img, "a+b")
f_root.truncate (self.root_img_size)
f_root.close ()
fd = open(self.root_img, 'a+b')
fd.truncate(self.root_img_size)
fd.close()
elif self.vm.updateable:
# if not updateable, just use template's disk
@ -142,9 +143,9 @@ class XenVMStorage(qubes.storage.VMStorage):
def resize_private_img(self, size):
f_private = open(self.private_img, "a+b")
f_private.truncate(size)
f_private.close()
fd = open(self.private_img, 'a+b')
fd.truncate(size)
fd.close()
# find loop device if any
p = subprocess.Popen(

View File

@ -14,7 +14,8 @@ import qubes.events
#: :py:obj:`True` if running in dom0, :py:obj:`False` otherwise
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
try:
@ -26,7 +27,8 @@ except libvirt.libvirtError:
pass
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:
# git returned nonzero, we are outside git repo
pass
@ -120,8 +122,10 @@ class QubesTestCase(unittest.TestCase):
:param emitter: emitter which is being checked
:type emitter: :py:class:`TestEmitter`
:param str event: event identifier
:param list args: when given, all items must appear in args passed to event
:param list kwargs: when given, all items must appear in kwargs passed to event
:param list args: when given, all items must appear in args passed to \
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:
@ -143,8 +147,10 @@ class QubesTestCase(unittest.TestCase):
:param emitter: emitter which is being checked
:type emitter: :py:class:`TestEmitter`
:param str event: event identifier
:param list args: when given, all items must appear in args passed to event
:param list kwargs: when given, all items must appear in kwargs passed to event
:param list args: when given, all items must appear in args passed to \
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:

View File

@ -82,7 +82,8 @@ class TC_10_property(qubes.tests.QubesTestCase):
def test_023_get_default_func(self):
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)
self.assertEqual(holder.testprop1, 'defaultvalue')
@ -202,7 +203,8 @@ class TC_20_PropertyHolder(qubes.tests.QubesTestCase):
expected_prop1.text = 'testvalue1'
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)
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.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):
self.vms.add(self.testvm1)
@ -324,8 +327,10 @@ class TC_30_VMCollection(qubes.tests.QubesTestCase):
class TC_90_Qubes(qubes.tests.QubesTestCase):
@qubes.tests.skipUnlessDom0
def test_000_init_empty(self):
try: os.unlink('/tmp/qubestest.xml')
except: pass
try:
os.unlink('/tmp/qubestest.xml')
except:
pass
app = qubes.Qubes('/tmp/qubestest.xml')
@qubes.tests.skipUnlessGit

View File

@ -143,8 +143,8 @@ class ANSITestResult(unittest.TestResult):
super(ANSITestResult, self).addUnexpectedSuccess(test)
if self.showAll:
self.stream.writeln(
'{color[yellow]}{color[bold]}unexpected success{color[normal]}'.format(
color=self.color))
'{color[yellow]}{color[bold]}unexpected success'
'{color[normal]}'.format(color=self.color))
elif self.dots:
self.stream.write(
'{color[yellow]}{color[bold]}u{color[normal]}'.format(

View File

@ -1,4 +1,28 @@
#!/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
@ -92,7 +116,8 @@ class DeviceCollection(object):
raise KeyError(
'device {!r} of class {} already attached to {!r}'.format(
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._vm.fire_event('device-attached:{}'.format(self._class), device)
@ -107,7 +132,8 @@ class DeviceCollection(object):
raise KeyError(
'device {!r} of class {} not attached to {!r}'.format(
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._vm.fire_event('device-detached:{}'.format(self._class), device)
@ -161,7 +187,8 @@ class BaseVM(qubes.PropertyHolder):
self.tags = tags
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()):
if not key in all_names:
raise AttributeError(
@ -184,8 +211,8 @@ class BaseVM(qubes.PropertyHolder):
vm_cls = QubesVmClasses[vm_type]
if 'template' in kwargs:
if not vm_cls.is_template_compatible(kwargs['template']):
raise QubesException("Template not compatible with selected "
"VM type")
raise QubesException(
'Template not compatible with selected VM type')
vm = vm_cls(qid=qid, collection=self, **kwargs)
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 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:
return cls(app)
@ -233,7 +259,8 @@ class BaseVM(qubes.PropertyHolder):
# services
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, ...)
for parent in xml.xpath('./devices'):
@ -375,7 +402,8 @@ class BaseVM(qubes.PropertyHolder):
args['vcpus'] = str(self.vcpus)
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
args['maxmem'] = args['mem']
@ -386,9 +414,10 @@ class BaseVM(qubes.PropertyHolder):
args['dns1'] = self.netvm.gateway
args['dns2'] = self.secondary_dns
args['netmask'] = self.netmask
args['netdev'] = lxml.etree.tostring(self.lvxml_net_dev(self.ip, self.mac, self.netvm))
args['disable_network1'] = '';
args['disable_network2'] = '';
args['netdev'] = lxml.etree.tostring(
self.lvxml_net_dev(self.ip, self.mac, self.netvm))
args['disable_network1'] = ''
args['disable_network2'] = ''
else:
args['ip'] = ''
args['mac'] = ''
@ -397,15 +426,16 @@ class BaseVM(qubes.PropertyHolder):
args['dns2'] = ''
args['netmask'] = ''
args['netdev'] = ''
args['disable_network1'] = '<!--';
args['disable_network2'] = '-->';
args['disable_network1'] = '<!--'
args['disable_network2'] = '-->'
args.update(self.storage.get_config_params())
if hasattr(self, 'kernelopts'):
args['kernelopts'] = self.kernelopts
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'
@ -415,8 +445,10 @@ class BaseVM(qubes.PropertyHolder):
If :py:attr:`qubes.vm.qubesvm.QubesVM.uses_custom_config` is true, this
does nothing.
:param str file_path: Path to file to create (default: :py:attr:`qubes.vm.qubesvm.QubesVM.conf_file`)
:param bool prepare_dvm: If we are in the process of preparing DisposableVM
:param str file_path: Path to file to create \
(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:
@ -435,7 +467,8 @@ class BaseVM(qubes.PropertyHolder):
if prepare_dvm:
template_params['name'] = '%NAME%'
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)
# FIXME: This is only for debugging purposes
@ -470,11 +503,10 @@ class BaseVM(qubes.PropertyHolder):
root = lxml.etree.Element(
"QubesFirewallRules",
policy = "allow" if conf["allow"] else "deny",
dns = "allow" if conf["allowDns"] else "deny",
icmp = "allow" if conf["allowIcmp"] else "deny",
yumProxy = "allow" if conf["allowYumProxy"] else "deny"
)
policy=("allow" if conf["allow"] else "deny"),
dns=("allow" if conf["allowDns"] else "deny"),
icmp=("allow" if conf["allowIcmp"] else "deny"),
yumProxy=("allow" if conf["allowYumProxy"] else "deny"))
for rule in conf["rules"]:
# For backward compatibility
@ -514,7 +546,8 @@ class BaseVM(qubes.PropertyHolder):
os.path.basename(sys.argv[0]), err)
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']:
self.services['yum-proxy-setup'] = True
else:
@ -531,7 +564,12 @@ class BaseVM(qubes.PropertyHolder):
return os.path.exists(self.firewall_conf)
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):
conf = self.get_firewall_defaults()
@ -582,10 +620,10 @@ class BaseVM(qubes.PropertyHolder):
"%s")):
continue
else:
del(rule["expire"])
del rule["expire"]
del(rule["port"])
del(rule["toport"])
del rule["port"]
del rule["toport"]
conf["rules"].append(rule)

View File

@ -122,7 +122,8 @@ class QubesVM(qubes.vm.BaseVM):
`None`, machine is disconnected. When absent, domain uses default
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.')
qid = qubes.property('qid', type=int,
@ -150,20 +151,22 @@ class QubesVM(qubes.vm.BaseVM):
firewall_conf = qubes.property('firewall_conf', type=str,
default='firewall.xml')
installed_by_rpm = qubes.property('installed_by_rpm', type=bool, default=False,
setter=qubes.property.bool,
installed_by_rpm = qubes.property('installed_by_rpm',
type=bool, setter=qubes.property.bool,
default=False,
doc='''If this domain's image was installed from package tracked by
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.')
maxmem = qubes.property('maxmem', type=int, default=None,
doc='''Maximum amount of memory available for this VM (for the purpose
of the memory balancer).''')
internal = qubes.property('internal', type=bool, default=False,
setter=qubes.property.bool,
internal = qubes.property('internal', default=False,
type=bool, setter=qubes.property.bool,
doc='''Internal VM (not shown in qubes-manager, don't create appmenus
entries.''')
@ -213,18 +216,18 @@ class QubesVM(qubes.vm.BaseVM):
failed. Operating system inside VM should be able to boot in this
time.''')
autostart = qubes.property('autostart', type=bool, default=False,
setter=qubes.property.bool,
doc='''Setting this to `True` means that VM should be autostarted on dom0
boot.''')
autostart = qubes.property('autostart', default=False,
type=bool, setter=qubes.property.bool,
doc='''Setting this to `True` means that VM should be autostarted on
dom0 boot.''')
# XXX I don't understand backups
include_in_backups = qubes.property('include_in_backups', type=bool, default=True,
setter=qubes.property.bool,
include_in_backups = qubes.property('include_in_backups', default=True,
type=bool, setter=qubes.property.bool,
doc='If this domain is to be included in default backup.')
backup_content = qubes.property('backup_content', type=bool, default=False,
setter=qubes.property.bool,
backup_content = qubes.property('backup_content', default=False,
type=bool, setter=qubes.property.bool,
doc='FIXME')
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)
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')),
doc='FIXME')
@ -279,12 +283,14 @@ class QubesVM(qubes.vm.BaseVM):
# XXX _update_libvirt_domain?
try:
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:
self._libvirt_domain = vmm.libvirt_conn.lookupByName(self.name)
self.uuid = uuid.UUID(bytes=self._libvirt_domain.UUID())
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()
else:
raise
@ -303,7 +309,8 @@ class QubesVM(qubes.vm.BaseVM):
# XXX this should go to to AppVM?
@property
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
@ -427,7 +434,7 @@ class QubesVM(qubes.vm.BaseVM):
# Always set if meminfo-writer should be active or not
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
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):
# we are changing to default 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)
@qubes.events.handler('property-set:netvm')
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")
if self.netvm is not None:
@ -490,8 +499,10 @@ class QubesVM(qubes.vm.BaseVM):
if not self._do_not_reset_firewall:
# Set also firewall to block all traffic as discussed in #370
if os.path.exists(self.firewall_conf):
shutil.copy(self.firewall_conf, os.path.join(system_path["qubes_base_dir"],
"backup", "%s-firewall-%s.xml" % (self.name,
shutil.copy(self.firewall_conf,
os.path.join(system_path["qubes_base_dir"],
"backup",
"%s-firewall-%s.xml" % (self.name,
time.strftime('%Y-%m-%d-%H:%M:%S'))))
self.write_firewall_conf({'allow': False, 'allowDns': False,
'allowIcmp': False, 'allowYumProxy': False, 'rules': []})
@ -554,13 +565,15 @@ class QubesVM(qubes.vm.BaseVM):
new_conf, old_conf)
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()
@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',
('enable' if value else 'disable'),
'qubes-vm@{}.service'.format(self.name)]):
@ -576,8 +589,10 @@ class QubesVM(qubes.vm.BaseVM):
try:
# TODO: libvirt-ise
subprocess.check_call(['sudo', system_path["qubes_pciback_cmd"], pci])
subprocess.check_call(['sudo', 'xl', 'pci-attach', str(self.xid), pci])
subprocess.check_call(
['sudo', system_path["qubes_pciback_cmd"], pci])
subprocess.check_call(
['sudo', 'xl', 'pci-attach', str(self.xid), pci])
except Exception as e:
print >>sys.stderr, "Failed to attach PCI device on the fly " \
"(%s), changes will be seen after VM restart" % str(e)
@ -592,7 +607,8 @@ class QubesVM(qubes.vm.BaseVM):
p = subprocess.Popen(['xl', 'pci-list', str(self.xid)],
stdout=subprocess.PIPE)
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:
print >>sys.stderr, "Device %s already detached" % pci
return
@ -600,7 +616,8 @@ class QubesVM(qubes.vm.BaseVM):
try:
self.run_service("qubes.DetachPciDevice",
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:
print >>sys.stderr, "Failed to detach PCI device on the fly " \
"(%s), changes will be seen after VM restart" % str(e)
@ -620,7 +637,8 @@ class QubesVM(qubes.vm.BaseVM):
: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":
raise QubesException("VM is already running!")
@ -631,7 +649,8 @@ class QubesVM(qubes.vm.BaseVM):
if self.netvm is not None:
if self.netvm.qid != 0:
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._update_libvirt_domain()
@ -643,19 +662,22 @@ class QubesVM(qubes.vm.BaseVM):
try:
got_memory = qmemman_client.request_memory(mem_required)
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:
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
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:
nd.dettach()
except libvirt.libvirtError:
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_INTERNAL_ERROR:
# allready detached
if vmm.libvirt_conn.virConnGetLastError()[0] == \
libvirt.VIR_ERR_INTERNAL_ERROR:
# already detached
pass
else:
raise
@ -675,7 +697,8 @@ class QubesVM(qubes.vm.BaseVM):
if vm.is_proxyvm() and vm.is_running():
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))
@ -689,13 +712,15 @@ class QubesVM(qubes.vm.BaseVM):
if qmemman_present:
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)
if not preparing_dvm:
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)
@ -740,7 +765,8 @@ class QubesVM(qubes.vm.BaseVM):
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():
raise QubesException("VM not running!")
@ -775,16 +801,22 @@ class QubesVM(qubes.vm.BaseVM):
:param str command: the command to be run
: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 bool passio: FIXME
:param bool passio_popen: if :py:obj:`True`, :py:class:`subprocess.Popen` object has connected ``stdin`` and ``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 bool passio_popen: if :py:obj:`True`, \
:py:class:`subprocess.Popen` object has connected ``stdin`` and \
``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 bool wait: if :py:obj:`True`, wait for command completion
: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:
@ -796,15 +828,18 @@ class QubesVM(qubes.vm.BaseVM):
try:
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)
except (IOError, OSError, QubesException) as err:
raise QubesException("Error while starting the '{0}' VM: {1}".format(self.name, err))
except (IOError, OSError, QubesException) as e:
raise QubesException(
'Error while starting the {!r} VM: {!s}'.format(
self.name, e))
except (MemoryError) as err:
raise QubesException("Not enough memory to start '{0}' VM! "
"Close one or more running VMs and try "
"again.".format(self.name))
raise QubesException('Not enough memory to start {!r} VM! '
'Close one or more running VMs and try again.'.format(
self.name))
if self.is_paused():
raise QubesException("VM is paused")
@ -812,22 +847,26 @@ class QubesVM(qubes.vm.BaseVM):
raise QubesException(
"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 \
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:
args += [ "-l", localcmd]
args += ['-l', localcmd]
if filter_esc:
args += ["-t"]
args += ['-t']
if os.isatty(sys.stderr.fileno()):
args += ["-T"]
args += ['-T']
# TODO: QSB#13
if passio:
if os.name == 'nt':
# wait for qrexec-client to exit, otherwise client is not properly attached to console
# if qvm-run is executed from cmd.exe
# wait for qrexec-client to exit, otherwise client is not
# properly attached to console if qvm-run is executed from
# cmd.exe
ret = subprocess.call(args)
exit(ret)
os.execv(system_path["qrexec_client_path"], args)
@ -910,13 +949,14 @@ class QubesVM(qubes.vm.BaseVM):
guid_cmd += ['-q']
retcode = subprocess.call(guid_cmd)
if (retcode != 0) :
raise QubesException("Cannot start qubes-guid!")
if retcode != 0:
raise QubesException('Cannot start qubes-guid!')
self.log.info('Sending monitor layout')
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:
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)
retcode = subprocess.call([system_path["qrexec_daemon_path"]] +
qrexec_args, env=qrexec_env)
if (retcode != 0) :
raise OSError("Cannot execute qrexec-daemon!")
if retcode != 0:
raise OSError('Cannot execute qrexec-daemon!')
def start_qubesdb(self):
@ -998,7 +1038,8 @@ class QubesVM(qubes.vm.BaseVM):
os.path.join(self.dir_path,
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"):
os.symlink(self.label.icon_path, self.icon_path)
else:
@ -1021,8 +1062,12 @@ class QubesVM(qubes.vm.BaseVM):
# FIXME move this to qubes.storage.xen.XenVMStorage
retcode = 0
if self.is_running():
retcode = self.run("while [ \"`blockdev --getsize64 /dev/xvdb`\" -lt {0} ]; do ".format(size) +
"head /dev/xvdb > /dev/null; sleep 0.2; done; resize2fs /dev/xvdb", user="root", wait=True)
retcode = self.run('''
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:
raise QubesException("resize2fs failed")
@ -1123,7 +1168,8 @@ class QubesVM(qubes.vm.BaseVM):
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
see its documentation.
@ -1195,9 +1241,12 @@ class QubesVM(qubes.vm.BaseVM):
{ 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):
'''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
'''
@ -1240,7 +1290,8 @@ class QubesVM(qubes.vm.BaseVM):
def is_paused(self):
'''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
'''
@ -1251,7 +1302,8 @@ class QubesVM(qubes.vm.BaseVM):
def is_guid_running(self):
'''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
'''
xid = self.xid
@ -1265,7 +1317,8 @@ class QubesVM(qubes.vm.BaseVM):
def is_qrexec_running(self):
'''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
'''
if self.xid < 0:
@ -1278,7 +1331,8 @@ class QubesVM(qubes.vm.BaseVM):
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
'''
@ -1402,7 +1456,8 @@ class QubesVM(qubes.vm.BaseVM):
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]
:rtype: FIXME
@ -1445,7 +1500,8 @@ class QubesVM(qubes.vm.BaseVM):
return None
# 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 != '':
return datetime.datetime.fromtimestamp(float(start_time))
else:
@ -1454,7 +1510,8 @@ class QubesVM(qubes.vm.BaseVM):
# XXX this probably should go to AppVM
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.
:rtype: bool
@ -1485,7 +1542,8 @@ class QubesVM(qubes.vm.BaseVM):
# FIXME
# 51712 (0xCA00) is xvda
# 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
@ -1493,7 +1551,8 @@ class QubesVM(qubes.vm.BaseVM):
def is_networked(self):
'''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
'''
@ -1555,7 +1614,8 @@ class QubesVM(qubes.vm.BaseVM):
# TODO: Currently the whole qmemman is quite Xen-specific, so stay with
# xenstore for it until decided otherwise
if qmemman_present:
vmm.xs.set_permissions('', '/local/domain/{0}/memory'.format(self.xid),
vmm.xs.set_permissions('',
'/local/domain/{}/memory'.format(self.xid),
[{'dom': self.xid}])
self.fire_event('qdb-created')
@ -1570,7 +1630,8 @@ class QubesVM(qubes.vm.BaseVM):
self._libvirt_domain = vmm.libvirt_conn.defineXML(domain_config)
self.uuid = uuid.UUID(bytes=self._libvirt_domain.UUID())
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
# domain...
pass
@ -1592,13 +1653,15 @@ class QubesVM(qubes.vm.BaseVM):
dev_basepath = '/local/domain/%d/device/vif' % self.xid
for dev in self.app.vmm.xs.ls('', dev_basepath):
# 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():
# 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
# 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 = {
#
##### 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
# "_do_not_reset_firewall": { "func": lambda x: False },
# XXX WTF?
# "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: \
# os.path.join(system_path["qubes_kernels_base_dir"],
# self.kernel) if self.kernel is not None \