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,8 +710,9 @@ class property(object):
:throws AttributeError: always
'''
raise AttributeError('setting {} property on {} instance is forbidden'.format(
prop.__name__, self.__class__.__name__))
raise AttributeError(
'setting {} property on {} instance is forbidden'.format(
prop.__name__, self.__class__.__name__))
@staticmethod
@ -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(
self.__class__.__name__))
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(
self.__class__.__name__))
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

@ -22,7 +22,7 @@
#
#
qubes_base_dir = "/var/lib/qubes"
qubes_base_dir = "/var/lib/qubes"
system_path = {
'qubes_guid_path': '/usr/bin/qubes-guid',
'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')),
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(
@ -166,7 +167,7 @@ class XenVMStorage(qubes.storage.VMStorage):
# TODO: move rootcow_img to this class; the same for vm.is_outdated()
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)
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
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:
@ -166,7 +172,7 @@ class QubesTestCase(unittest.TestCase):
Schema can be given in a couple of ways:
- 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*
keyword argument.

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(
@ -165,7 +165,7 @@ class ANSITestResult(unittest.TestResult):
def printErrorList(self, flavour, errors):
for test, err in errors:
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('%s' % err)

View File

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

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:
@ -528,10 +561,15 @@ class BaseVM(qubes.PropertyHolder):
return True
def has_firewall(self):
return os.path.exists (self.firewall_conf)
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,11 +499,13 @@ 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': []})
'allowIcmp': False, 'allowYumProxy': False, 'rules': []})
else:
new_netvm.connected_vms.add(self)
@ -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,15 +607,17 @@ 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
vmdev = m.group(1)
try:
self.run_service("qubes.DetachPciDevice",
user="root", input="00:%s" % vmdev)
subprocess.check_call(['sudo', 'xl', 'pci-detach', str(self.xid), pci])
user="root", input="00:%s" % vmdev)
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():
self.start_guid(verbose = verbose, notify_function = notify_function)
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)
@ -839,7 +878,7 @@ class QubesVM(qubes.vm.BaseVM):
call_kwargs['stderr'] = null
if passio_popen:
popen_kwargs={'stdout': subprocess.PIPE}
popen_kwargs = {'stdout': subprocess.PIPE}
popen_kwargs['stdin'] = subprocess.PIPE
if passio_stderr:
popen_kwargs['stderr'] = subprocess.PIPE
@ -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
'''
@ -1539,7 +1598,7 @@ class QubesVM(qubes.vm.BaseVM):
tzname = qubes.utils.get_timezone()
if tzname:
self.qdb.write("/qubes-timezone", tzname)
self.qdb.write("/qubes-timezone", tzname)
for srv in self.services.keys():
# 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
# xenstore for it until decided otherwise
if qmemman_present:
vmm.xs.set_permissions('', '/local/domain/{0}/memory'.format(self.xid),
[{ 'dom': 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 \