diff --git a/qubes/__init__.py b/qubes/__init__.py index 97a84e10..a445872f 100644 --- a/qubes/__init__.py +++ b/qubes/__init__.py @@ -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: (subject, event, name, newvalue[, oldvalue]) + .. event:: property-set: \ + (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: (subject, event, name, newvalue[, oldvalue]) + .. event:: property-pre-set: \ + (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: (subject, event, name[, oldvalue]) + .. event:: property-del: \ + (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: (subject, event, name[, oldvalue]) + .. event:: property-pre-del: \ + (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 diff --git a/qubes/config.py b/qubes/config.py index 1277e244..1018fc0f 100644 --- a/qubes/config.py +++ b/qubes/config.py @@ -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', diff --git a/qubes/dochelpers.py b/qubes/dochelpers.py index 6791d799..e2108c31 100644 --- a/qubes/dochelpers.py +++ b/qubes/dochelpers.py @@ -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)) diff --git a/qubes/events.py b/qubes/events.py index 4eb03361..044047d5 100644 --- a/qubes/events.py +++ b/qubes/events.py @@ -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) diff --git a/qubes/ext/__init__.py b/qubes/ext/__init__.py index 1a98e339..724cc173 100644 --- a/qubes/ext/__init__.py +++ b/qubes/ext/__init__.py @@ -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): diff --git a/qubes/log.py b/qubes/log.py index 1e43f748..74e8f12f 100644 --- a/qubes/log.py +++ b/qubes/log.py @@ -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') diff --git a/qubes/rngdoc.py b/qubes/rngdoc.py index 8467d68a..79b0b639 100755 --- a/qubes/rngdoc.py +++ b/qubes/rngdoc.py @@ -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} `'.format(child.xml.get('name')), n) + childtable = [(':ref:`{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) diff --git a/qubes/storage/xen.py b/qubes/storage/xen.py index 79c10b24..97d3004a 100644 --- a/qubes/storage/xen.py +++ b/qubes/storage/xen.py @@ -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') diff --git a/qubes/tests/__init__.py b/qubes/tests/__init__.py index 06ae7ac7..b40e2627 100644 --- a/qubes/tests/__init__.py +++ b/qubes/tests/__init__.py @@ -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. diff --git a/qubes/tests/init.py b/qubes/tests/init.py index 4bd48e77..b3cf473e 100644 --- a/qubes/tests/init.py +++ b/qubes/tests/init.py @@ -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 diff --git a/qubes/tests/run.py b/qubes/tests/run.py index a5a2c573..5dedef70 100755 --- a/qubes/tests/run.py +++ b/qubes/tests/run.py @@ -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) diff --git a/qubes/tests/vm/qubesvm.py b/qubes/tests/vm/qubesvm.py index 02750824..515c2b8a 100644 --- a/qubes/tests/vm/qubesvm.py +++ b/qubes/tests/vm/qubesvm.py @@ -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 diff --git a/qubes/vm/__init__.py b/qubes/vm/__init__.py index 6facc05f..98b0674b 100644 --- a/qubes/vm/__init__.py +++ b/qubes/vm/__init__.py @@ -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 +# Copyright (C) 2011-2015 Marek Marczykowski-Górecki +# +# Copyright (C) 2014-2015 Wojtek Porczyk +# +# 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_network1'] = '' 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) diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py index 9999d397..b574fa64 100644 --- a/qubes/vm/qubesvm.py +++ b/qubes/vm/qubesvm.py @@ -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 \