diff --git a/qubes/__init__.py b/qubes/__init__.py index 0bf43c36..9914e7cc 100644 --- a/qubes/__init__.py +++ b/qubes/__init__.py @@ -57,6 +57,8 @@ import lxml.etree import qubes.config +import qubes.events +import qubes.exc import qubes.ext @@ -78,14 +80,6 @@ except ImportError: pass -class QubesException(Exception): - '''Exception that can be shown to the user''' - pass - - -import qubes.events - - class VMMConnection(object): '''Connection to Virtual Machine Manager (libvirt)''' @@ -103,8 +97,8 @@ class VMMConnection(object): @offline_mode.setter def offline_mode(self, value): if value and self._libvirt_conn is not None: - raise QubesException( - "Cannot change offline mode while already connected") + raise qubes.exc.QubesException( + 'Cannot change offline mode while already connected') self._offline_mode = value @@ -120,7 +114,8 @@ class VMMConnection(object): return if self._offline_mode: # Do not initialize in offline mode - raise QubesException("VMM operations disabled in offline mode") + raise qubes.exc.QubesException( + 'VMM operations disabled in offline mode') if 'xen.lowlevel.xs' in sys.modules: self._xs = xen.lowlevel.xs.xs() @@ -128,7 +123,7 @@ class VMMConnection(object): self._xc = xen.lowlevel.xc.xc() self._libvirt_conn = libvirt.open(qubes.config.defaults['libvirt_uri']) if self._libvirt_conn is None: - raise QubesException("Failed connect to libvirt driver") + raise qubes.exc.QubesException('Failed connect to libvirt driver') libvirt.registerErrorHandler(self._libvirt_error_handler, None) atexit.register(self._libvirt_conn.close) @@ -1371,7 +1366,7 @@ class Qubes(PropertyHolder): if isinstance(vm, qubes.vm.templatevm.TemplateVM): appvms = self.domains.get_vms_based_on(vm) if appvms: - raise QubesException( + raise qubes.exc.QubesException( 'Cannot remove template that has dependent AppVMs. ' 'Affected are: {}'.format(', '.join( vm.name for name in sorted(appvms)))) @@ -1399,8 +1394,9 @@ class Qubes(PropertyHolder): return 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 qubes.exc.QubesVMError(newvalue, + 'Cannot set {!r} as {!r} since it has ntpd enabled.'.format( + newvalue.name, name)) else: newvalue.services['ntpd'] = False @@ -1414,8 +1410,9 @@ class Qubes(PropertyHolder): 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 qubes.exc.QubesVMNotRunningError(newvalue, + 'Cannot change {!r} to domain that ' + 'is not running ({!r}).'.format(name, newvalue.name)) @qubes.events.handler('property-set:default_fw_netvm') diff --git a/qubes/exc.py b/qubes/exc.py new file mode 100644 index 00000000..58d6df20 --- /dev/null +++ b/qubes/exc.py @@ -0,0 +1,147 @@ +#!/usr/bin/python2 -O +# vim: fileencoding=utf-8 + +# +# The Qubes OS Project, https://www.qubes-os.org/ +# +# Copyright (C) 2015 Joanna Rutkowska +# Copyright (C) 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 OS exception hierarchy +''' + +class QubesException(Exception): + '''Exception that can be shown to the user''' + pass + + +class QubesVMNotFoundError(QubesException, KeyError): + '''Domain cannot be found in the system''' + def __init__(self, vmname): + super(QubesVMNotFoundError, self).__init__( + 'No such domain: {!r}'.format(vmname)) + self.vmname = vmname + + +class QubesVMError(QubesException): + '''Some problem with domain state.''' + def __init__(self, vm, msg): + super(QubesVMError, self).__init__(msg) + self.vm = vm + + +class QubesVMNotStartedError(QubesVMError): + '''Domain is not started. + + This exception is thrown when machine is halted, but should be started + (that is, either running or paused). + ''' + def __init__(self, vm, msg=None): + super(QubesVMNotStartedError, self).__init__(vm, + msg or 'Domain is powered off: {!r}'.format(vm.name)) + + +class QubesVMNotRunningError(QubesVMNotStartedError): + '''Domain is not running. + + This exception is thrown when machine should be running but is either + halted or paused. + ''' + def __init__(self, vm, msg=None): + super(QubesVMNotRunningError, self).__init__(vm, + msg or 'Domain not running (either powered off or paused): {!r}' \ + .format(vm.name)) + + +class QubesVMNotPausedError(QubesVMNotStartedError): + '''Domain is not paused. + + This exception is thrown when machine should be paused, but is not. + ''' + def __init__(self, vm, msg=None): + super(QubesVMNotPausedError, self).__init__(vm, + msg or 'Domain is not paused: {!r}'.format(vm.name)) + + +class QubesVMNotSuspendedError(QubesVMError): + '''Domain is not suspended. + + This exception is thrown when machine should be suspended but is either + halted or running. + ''' + def __init__(self, vm, msg=None): + super(QubesVMNotSuspendedError, self).__init__(vm, + msg or 'Domain is not suspended: {!r}'.format(vm.name)) + + +class QubesVMNotHaltedError(QubesVMError): + '''Domain is not halted. + + This exception is thrown when machine should be halted, but is not (either + running or paused). + ''' + def __init__(self, vm, msg=None): + super(QubesVMNotHaltedError, self).__init__(vm, + msg or 'Domain is not powered off: {!r}'.format(vm.name)) + + +class QubesNoTemplateError(QubesVMError): + '''Cannot start domain, because there is no template''' + def __init__(self, vm, msg=None): + super(QubesNoTemplateError, self).__init__( + msg or 'Template for the domain {!r} not found'.format(vm.name)) + + +class QubesValueError(QubesException, ValueError): + '''Cannot set some value, because it is invalid, out of bounds, etc.''' + pass + + +class QubesPropertyValueError(QubesValueError): + '''Cannot set value of qubes.property, because user-supplied value is wrong. + ''' + def __init__(self, holder, prop, value, msg=None): + super(QubesPropertyValueError, self).__init__( + msg or 'Invalid value {!r} for property {!r} of {!r}'.format( + value, prop.__name__, holder)) + self.holder = holder + self.prop = prop + self.value = value + + +class QubesNotImplementedError(QubesException, NotImplementedError): + '''Thrown at user when some feature is not implemented''' + def __init__(self, msg=None): + super(QubesNotImplementedError, self).__init__( + msg or 'This feature is not available') + + +class BackupCancelledError(QubesException): + '''Thrown at user when backup was manually cancelled''' + def __init__(self, msg=None): + super(BackupCancelledError, self).__init__( + msg or 'Backup cancelled') + + +class QubesMemoryError(QubesException, MemoryError): + '''Cannot start domain, because not enough memory is available''' + def __init__(self, vm, msg=None): + super(QubesMemoryError, self).__init__( + msg or 'Not enough memory to start domain {!r}'.format(vm.name)) + self.vm = vm diff --git a/qubes/storage/__init__.py b/qubes/storage/__init__.py index 09cb462b..bc520248 100644 --- a/qubes/storage/__init__.py +++ b/qubes/storage/__init__.py @@ -35,6 +35,7 @@ import subprocess import sys import qubes +import qubes.exc import qubes.utils BLKSIZE = 512 @@ -214,22 +215,22 @@ class VMStorage(object): def verify_files(self): if not os.path.exists(self.vm.dir_path): - raise qubes.QubesException( + raise qubes.exc.QubesVMError(self.vm, 'VM directory does not exist: {}'.format(self.vm.dir_path)) if hasattr(self.vm, 'root_img') and not os.path.exists(self.root_img): - raise qubes.QubesException( + raise qubes.exc.QubesVMError(self.vm, 'VM root image file does not exist: {}'.format(self.root_img)) if hasattr(self.vm, 'private_img') \ and not os.path.exists(self.private_img): - raise qubes.QubesException( + raise qubes.exc.QubesVMError(self.vm, 'VM private image file does not exist: {}'.format( self.private_img)) if self.modules_img is not None \ and not os.path.exists(self.modules_img): - raise qubes.QubesException( + raise qubes.exc.QubesVMError(self.vm, 'VM kernel modules image does not exists: {}'.format( self.modules_img)) diff --git a/qubes/tests/vm/adminvm.py b/qubes/tests/vm/adminvm.py index 655812ba..88846be5 100644 --- a/qubes/tests/vm/adminvm.py +++ b/qubes/tests/vm/adminvm.py @@ -25,6 +25,7 @@ import unittest import qubes +import qubes.exc import qubes.vm.adminvm import qubes.tests @@ -98,10 +99,10 @@ class TC_00_AdminVM(qubes.tests.QubesTestCase): self.assertEqual(self.vm.get_private_img_sz(), 0) def test_310_start(self): - with self.assertRaises(qubes.QubesException): + with self.assertRaises(qubes.exc.QubesException): self.vm.start() @unittest.skip('this functionality is undecided') def test_311_suspend(self): - with self.assertRaises(qubes.QubesException): + with self.assertRaises(qubes.exc.QubesException): self.vm.suspend() diff --git a/qubes/tests/vm/qubesvm.py b/qubes/tests/vm/qubesvm.py index 8b764324..11aacd32 100644 --- a/qubes/tests/vm/qubesvm.py +++ b/qubes/tests/vm/qubesvm.py @@ -26,6 +26,7 @@ import unittest import qubes +import qubes.exc import qubes.config import qubes.vm.qubesvm @@ -89,13 +90,13 @@ class TC_00_setters(qubes.tests.QubesTestCase): def test_014_setter_name_running(self): self.vm.running = True - with self.assertRaises(qubes.QubesException): + with self.assertRaises(qubes.exc.QubesVMNotHaltedError): qubes.vm.qubesvm._setter_name(self.vm, self.prop, 'testname') def test_015_setter_name_installed_by_rpm(self): # pylint: disable=invalid-name self.vm.installed_by_rpm = True - with self.assertRaises(qubes.QubesException): + with self.assertRaises(qubes.exc.QubesException): qubes.vm.qubesvm._setter_name(self.vm, self.prop, 'testname') diff --git a/qubes/tools/qvm_kill.py b/qubes/tools/qvm_kill.py index bb36e754..89ce4f51 100644 --- a/qubes/tools/qvm_kill.py +++ b/qubes/tools/qvm_kill.py @@ -26,7 +26,7 @@ import sys -import qubes +import qubes.exc import qubes.tools parser = qubes.tools.QubesArgumentParser( @@ -45,7 +45,7 @@ def main(args=None): try: args.vm.force_shutdown() - except (IOError, OSError, qubes.QubesException) as e: + except (IOError, OSError, qubes.exc.QubesException) as e: parser.error_runtime(str(e)) return True diff --git a/qubes/tools/qvm_start.py b/qubes/tools/qvm_start.py index 8e3c8de3..37248709 100644 --- a/qubes/tools/qvm_start.py +++ b/qubes/tools/qvm_start.py @@ -131,11 +131,7 @@ def main(args=None): preparing_dvm=args.preparing_dvm, start_guid=args.start_guid) # notify_function= - except MemoryError: - # TODO tray - parser.error_runtime( - 'not enough memory to start domain {!r}'.format(vm.name)) - except qubes.QubesException as e: + except qubes.exc.QubesException as e: parser.error_runtime('Qubes error: {!r}'.format(e)) return True diff --git a/qubes/utils.py b/qubes/utils.py index d3125119..eef40e3c 100644 --- a/qubes/utils.py +++ b/qubes/utils.py @@ -32,7 +32,7 @@ import docutils import docutils.core import docutils.io -import qubes +import qubes.exc def get_timezone(): @@ -103,4 +103,4 @@ def parse_size(size): size = size[:-len(unit)].strip() return int(size)*multiplier - raise qubes.QubesException("Invalid size: {0}.".format(size)) + raise qubes.exc.QubesException("Invalid size: {0}.".format(size)) diff --git a/qubes/vm/__init__.py b/qubes/vm/__init__.py index b4a05aa4..2d91526d 100644 --- a/qubes/vm/__init__.py +++ b/qubes/vm/__init__.py @@ -291,8 +291,7 @@ class BaseVM(qubes.PropertyHolder): dev_match = re.match(r'([0-9a-f]+):([0-9a-f]+)\.([0-9a-f]+)', address) if not dev_match: - raise qubes.QubesException( - 'Invalid PCI device address: {}'.format(address)) + raise ValueError('Invalid PCI device address: {!r}'.format(address)) hostdev = lxml.etree.Element('hostdev', type='pci', managed='yes') source = lxml.etree.Element('source') diff --git a/qubes/vm/adminvm.py b/qubes/vm/adminvm.py index c1e123d9..c419bd38 100644 --- a/qubes/vm/adminvm.py +++ b/qubes/vm/adminvm.py @@ -25,6 +25,7 @@ # import qubes +import qubes.exc import qubes.vm.qubesvm class AdminVM(qubes.vm.qubesvm.QubesVM): @@ -150,7 +151,7 @@ class AdminVM(qubes.vm.qubesvm.QubesVM): .. seealso: :py:meth:`qubes.vm.qubesvm.QubesVM.start` ''' # pylint: disable=unused-argument - raise qubes.QubesException('Cannot start Dom0 fake domain!') + raise qubes.exc.QubesVMError('Cannot start Dom0 fake domain!') def suspend(self): diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py index dc4c4dfe..d57c0930 100644 --- a/qubes/vm/qubesvm.py +++ b/qubes/vm/qubesvm.py @@ -42,8 +42,7 @@ import libvirt import qubes import qubes.config -#import qubes.qmemman -#import qubes.qmemman_algo +import qubes.exc import qubes.storage import qubes.utils import qubes.vm @@ -78,12 +77,13 @@ def _setter_name(self, prop, value): raise ValueError('{} value contains illegal characters'.format( prop.__name__)) if self.is_running(): - raise qubes.QubesException('Cannot change name of running VM') + raise qubes.exc.QubesVMNotHaltedError( + self, 'Cannot change name of running VM') try: if self.installed_by_rpm: - raise qubes.QubesException('Cannot rename VM installed by RPM -- ' - 'first clone VM and then use yum to remove package.') + raise qubes.exc.QubesException('Cannot rename VM installed by RPM ' + '-- first clone VM and then use yum to remove package.') except AttributeError: pass @@ -97,10 +97,11 @@ def _setter_kernel(self, prop, value): qubes.config.system_path['qubes_kernels_base_dir'], value) if not os.path.exists(dirname): - raise qubes.QubesException('Kernel {!r} not installed'.format(value)) + raise qubes.exc.QubesPropertyValueError(self, prop, value, + 'Kernel {!r} not installed'.format(value)) for filename in ('vmlinuz', 'modules.img'): if not os.path.exists(os.path.join(dirname, filename)): - raise qubes.QubesException( + raise qubes.exc.QubesPropertyValueError( 'Kernel {!r} not properly installed: missing {!r} file'.format( value, filename)) return value @@ -557,8 +558,9 @@ class QubesVM(qubes.vm.BaseVM): # pylint: disable=unused-argument if self.is_running() and new_netvm is not None \ and not new_netvm.is_running(): - raise qubes.QubesException( - 'Cannot dynamically attach to stopped NetVM') + raise qubes.exc.QubesVMNotStartedError(new_netvm, + 'Cannot dynamically attach to stopped NetVM: {!r}'.format( + new_netvm)) if self.netvm is not None: del self.netvm.connected_vms[self] @@ -601,7 +603,8 @@ class QubesVM(qubes.vm.BaseVM): # pylint: disable=unused-argument # TODO not self.is_stopped() would be more appropriate if self.is_running(): - raise qubes.QubesException('Cannot change name of running domain') + raise qubes.exc.QubesVMNotHaltedError( + 'Cannot change name of running domain {!r}'.format(oldvalue)) @qubes.events.handler('property-set:name') @@ -640,7 +643,7 @@ class QubesVM(qubes.vm.BaseVM): if subprocess.call(['sudo', 'systemctl', ('enable' if value else 'disable'), 'qubes-vm@{}.service'.format(self.name)]): - raise qubes.QubesException( + raise qubes.exc.QubesException( 'Failed to set autostart for VM via systemctl') @@ -648,7 +651,7 @@ class QubesVM(qubes.vm.BaseVM): def on_device_pre_attached_pci(self, event, pci): # pylint: disable=unused-argument if not os.path.exists('/sys/bus/pci/devices/0000:{}'.format(pci)): - raise qubes.QubesException('Invalid PCI device: {}'.format(pci)) + raise qubes.exc.QubesException('Invalid PCI device: {}'.format(pci)) if not self.is_running(): return @@ -707,7 +710,7 @@ class QubesVM(qubes.vm.BaseVM): # Intentionally not used is_running(): eliminate also "Paused", # "Crashed", "Halting" if self.get_power_state() != 'Halted': - raise qubes.QubesException('VM is already running!') + raise qubes.exc.QubesVMNotHaltedError(self) self.log.info('Starting {}'.format(self.name)) @@ -732,8 +735,7 @@ class QubesVM(qubes.vm.BaseVM): raise IOError('Failed to connect to qmemman: {!s}'.format(e)) if not got_memory: qmemman_client.close() - raise MemoryError( - 'Insufficient memory to start VM {!r}'.format(self.name)) + raise qubes.exc.QubesMemoryError(self) # Bind pci devices to pciback driver for pci in self.devices['pci']: @@ -794,11 +796,12 @@ class QubesVM(qubes.vm.BaseVM): def shutdown(self): '''Shutdown domain. - :raises QubesException: when domain is already shut down. + :raises qubes.exc.QubesVMNotStartedError: \ + when domain is already shut down. ''' - if not self.is_running(): - raise qubes.QubesException("VM already stopped!") + if not self.is_running(): # TODO not self.is_halted() + raise qubes.exc.QubesVMNotStartedError(self) self.libvirt_domain.shutdown() @@ -806,11 +809,12 @@ class QubesVM(qubes.vm.BaseVM): def force_shutdown(self): '''Forcefuly shutdown (destroy) domain. - :raises QubesException: when domain is already shut down. + :raises qubes.exc.QubesVMNotStartedError: \ + when domain is already shut down. ''' if not self.is_running() and not self.is_paused(): - raise qubes.QubesException('VM already stopped!') + raise qubes.exc.QubesVMNotStartedError(self) self.libvirt_domain.destroy() @@ -818,15 +822,19 @@ class QubesVM(qubes.vm.BaseVM): def suspend(self): '''Suspend (pause) domain. - :raises qubes.QubesException: when domain is already shut down. - :raises NotImplemetedError: when domain has PCI devices attached. + :raises qubes.exc.QubesVMNotRunnignError: \ + when domain is already shut down. + :raises qubes.exc.QubesNotImplemetedError: \ + when domain has PCI devices attached. ''' if not self.is_running() and not self.is_paused(): - raise qubes.QubesException('VM already stopped!') + raise qubes.exc.QubesVMNotRunningError(self) if len(self.devices['pci']) > 0: - raise NotImplementedError() + raise qubes.exc.QubesNotImplementedError( + 'Cannot suspend domain {!r} which has PCI devices attached' \ + .format(self.name)) else: self.libvirt_domain.suspend() @@ -836,7 +844,7 @@ class QubesVM(qubes.vm.BaseVM): :py:meth:`suspend`.''' if not self.is_running(): - raise qubes.QubesException('VM not running!') + raise qubes.exc.QubesVMNotRunningError(self) self.suspend() @@ -844,18 +852,21 @@ class QubesVM(qubes.vm.BaseVM): def resume(self): '''Resume suspended domain. - :raises NotImplemetedError: when machine is alread suspended. + :raises qubes.exc.QubesVMNotSuspendedError: when machine is not paused + :raises qubes.exc.QubesVMError: when machine is suspended ''' if self.get_power_state() == "Suspended": - raise NotImplementedError() + raise qubes.exc.QubesVMError(self, + 'Cannot resume suspended domain {!r}'.format(self.name)) else: self.unpause() + def unpause(self): '''Resume (unpause) a domain''' if not self.is_paused(): - raise qubes.QubesException('VM not paused!') + raise qubes.exc.QubesVMNotPausedError(self) self.libvirt_domain.resume() @@ -891,28 +902,21 @@ class QubesVM(qubes.vm.BaseVM): null = None if not self.is_running() and not self.is_paused(): if not autostart: - raise qubes.QubesException('VM not running') + raise qubes.exc.QubesVMNotRunningError(self) - try: - if notify_function is not None: - notify_function('info', - 'Starting the {!r} VM...'.format(self.name)) - self.start(start_guid=gui, notify_function=notify_function) - - except (IOError, OSError, qubes.QubesException) as e: - raise qubes.QubesException( - 'Error while starting the {!r} VM: {!s}'.format( - self.name, e)) - except MemoryError: - raise qubes.QubesException('Not enough memory to start {!r} VM!' - ' Close one or more running VMs and try again.'.format( - self.name)) + if notify_function is not None: + notify_function('info', + 'Starting the {!r} VM...'.format(self.name)) + self.start(start_guid=gui, notify_function=notify_function) if self.is_paused(): - raise qubes.QubesException('VM is paused') + # XXX what about autostart? + raise qubes.exc.QubesVMNotRunningError( + self, 'Domain {!r} is paused'.format(self.name)) + if not self.is_qrexec_running(): - raise qubes.QubesException( - "Domain '{}': qrexec not connected.".format(self.name)) + raise qubes.exc.QubesVMError( + self, 'Domain {!r}: qrexec not connected'.format(self.name)) if gui and os.getenv("DISPLAY") is not None \ and not self.is_guid_running(): @@ -1018,7 +1022,8 @@ class QubesVM(qubes.vm.BaseVM): retcode = subprocess.call(guid_cmd) if retcode != 0: - raise qubes.QubesException('Cannot start qubes-guid!') + raise qubes.exc.QubesVMError(self, + 'Cannot start qubes-guid for domain {!r}'.format(self.name)) self.log.info('Sending monitor layout') @@ -1123,8 +1128,8 @@ class QubesVM(qubes.vm.BaseVM): def resize_private_img(self, size): '''Resize private image.''' - # TODO QubesValueError, not assert - assert size >= self.get_private_img_sz(), "Cannot shrink private.img" + if size >= self.get_private_img_sz(): + raise qubes.exc.QubesValueError('Cannot shrink private.img') # resize the image self.storage.resize_private_img(size) @@ -1139,8 +1144,9 @@ class QubesVM(qubes.vm.BaseVM): sleep 0.2; done; resize2fs /dev/xvdb'''.format(size), user="root", wait=True) + if retcode != 0: - raise qubes.QubesException('resize2fs failed') + raise qubes.exc.QubesException('resize2fs failed') def remove_from_disk(self): @@ -1155,8 +1161,9 @@ class QubesVM(qubes.vm.BaseVM): :param qubes.vm.qubesvm.QubesVM src: source VM ''' - if src.is_running(): - raise qubes.QubesException('Attempt to clone a running VM!') + if src.is_running(): # XXX what about paused? + raise qubes.exc.QubesVMNotHaltedError( + self, 'Cannot clone a running domain {!r}'.format(self.name)) self.storage.clone_disk_files(src, verbose=False) @@ -1184,10 +1191,8 @@ class QubesVM(qubes.vm.BaseVM): '''Attach network in this machine to it's netvm.''' if not self.is_running(): - raise qubes.QubesException('VM not running!') - - if self.netvm is None: - raise qubes.QubesException('NetVM not set!') + raise qubes.exc.QubesVMNotRunningError(self) + assert self.netvm is not None if not self.netvm.is_running(): self.log.info('Starting NetVM ({0})'.format(self.netvm.name)) @@ -1201,11 +1206,8 @@ class QubesVM(qubes.vm.BaseVM): '''Detach machine from it's netvm''' if not self.is_running(): - raise qubes.QubesException('VM not running!') - - if self.netvm is None: - raise qubes.QubesException('NetVM not set!') - + raise qubes.exc.QubesVMNotRunningError(self) + assert self.netvm is not None self.libvirt_domain.detachDevice(lxml.etree.ElementTree( self.lvxml_net_dev(self.ip, self.mac, self.netvm)).tostring()) @@ -1548,12 +1550,13 @@ class QubesVM(qubes.vm.BaseVM): if not os.path.exists( os.path.join(self.storage.kernels_dir, 'vmlinuz')): - raise qubes.QubesException('VM kernel does not exist: {0}'.format( - os.path.join(self.storage.kernels_dir, 'vmlinuz'))) + raise qubes.exc.QubesException( + 'VM kernel does not exist: {0}'.format( + os.path.join(self.storage.kernels_dir, 'vmlinuz'))) if not os.path.exists( os.path.join(self.storage.kernels_dir, 'initramfs')): - raise qubes.QubesException( + raise qubes.exc.QubesException( 'VM initramfs does not exist: {0}'.format( os.path.join(self.storage.kernels_dir, 'initramfs'))) diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 483c1787..1e8a61e9 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -199,6 +199,7 @@ fi %{python_sitelib}/qubes/config.py* %{python_sitelib}/qubes/dochelpers.py* %{python_sitelib}/qubes/events.py* +%{python_sitelib}/qubes/exc.py* %{python_sitelib}/qubes/log.py* %{python_sitelib}/qubes/plugins.py* %{python_sitelib}/qubes/rngdoc.py*