core3: add different exceptions

From now on there are different exceptions which can be raise on
different occasions.

fixes QubesOS/qubes-issues#1279
This commit is contained in:
Wojtek Porczyk 2015-10-14 22:02:11 +02:00
parent 4cf56b40bb
commit 96efb4568a
12 changed files with 248 additions and 101 deletions

View File

@ -57,6 +57,8 @@ import lxml.etree
import qubes.config import qubes.config
import qubes.events
import qubes.exc
import qubes.ext import qubes.ext
@ -78,14 +80,6 @@ except ImportError:
pass pass
class QubesException(Exception):
'''Exception that can be shown to the user'''
pass
import qubes.events
class VMMConnection(object): class VMMConnection(object):
'''Connection to Virtual Machine Manager (libvirt)''' '''Connection to Virtual Machine Manager (libvirt)'''
@ -103,8 +97,8 @@ class VMMConnection(object):
@offline_mode.setter @offline_mode.setter
def offline_mode(self, value): def offline_mode(self, value):
if value and self._libvirt_conn is not None: if value and self._libvirt_conn is not None:
raise QubesException( raise qubes.exc.QubesException(
"Cannot change offline mode while already connected") 'Cannot change offline mode while already connected')
self._offline_mode = value self._offline_mode = value
@ -120,7 +114,8 @@ class VMMConnection(object):
return return
if self._offline_mode: if self._offline_mode:
# Do not initialize in 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: if 'xen.lowlevel.xs' in sys.modules:
self._xs = xen.lowlevel.xs.xs() self._xs = xen.lowlevel.xs.xs()
@ -128,7 +123,7 @@ class VMMConnection(object):
self._xc = xen.lowlevel.xc.xc() self._xc = xen.lowlevel.xc.xc()
self._libvirt_conn = libvirt.open(qubes.config.defaults['libvirt_uri']) self._libvirt_conn = libvirt.open(qubes.config.defaults['libvirt_uri'])
if self._libvirt_conn is None: 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) libvirt.registerErrorHandler(self._libvirt_error_handler, None)
atexit.register(self._libvirt_conn.close) atexit.register(self._libvirt_conn.close)
@ -1371,7 +1366,7 @@ class Qubes(PropertyHolder):
if isinstance(vm, qubes.vm.templatevm.TemplateVM): if isinstance(vm, qubes.vm.templatevm.TemplateVM):
appvms = self.domains.get_vms_based_on(vm) appvms = self.domains.get_vms_based_on(vm)
if appvms: if appvms:
raise QubesException( raise qubes.exc.QubesException(
'Cannot remove template that has dependent AppVMs. ' 'Cannot remove template that has dependent AppVMs. '
'Affected are: {}'.format(', '.join( 'Affected are: {}'.format(', '.join(
vm.name for name in sorted(appvms)))) vm.name for name in sorted(appvms))))
@ -1399,8 +1394,9 @@ class Qubes(PropertyHolder):
return return
if 'ntpd' in newvalue.services: if 'ntpd' in newvalue.services:
if newvalue.services['ntpd']: if newvalue.services['ntpd']:
raise QubesException('Cannot set {!r} as {!r} property since ' raise qubes.exc.QubesVMError(newvalue,
'it has ntpd enabled.'.format(newvalue, name)) 'Cannot set {!r} as {!r} since it has ntpd enabled.'.format(
newvalue.name, name))
else: else:
newvalue.services['ntpd'] = False newvalue.services['ntpd'] = False
@ -1414,8 +1410,9 @@ class Qubes(PropertyHolder):
if newvalue is not None and oldvalue is not None \ if newvalue is not None and oldvalue is not None \
and oldvalue.is_running() and not newvalue.is_running() \ and oldvalue.is_running() and not newvalue.is_running() \
and self.domains.get_vms_connected_to(oldvalue): and self.domains.get_vms_connected_to(oldvalue):
raise QubesException('Cannot change default_netvm to domain that ' raise qubes.exc.QubesVMNotRunningError(newvalue,
'is not running ({!r}).'.format(newvalue)) 'Cannot change {!r} to domain that '
'is not running ({!r}).'.format(name, newvalue.name))
@qubes.events.handler('property-set:default_fw_netvm') @qubes.events.handler('property-set:default_fw_netvm')

147
qubes/exc.py Normal file
View File

@ -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 <joanna@invisiblethingslab.com>
# Copyright (C) 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 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

View File

@ -35,6 +35,7 @@ import subprocess
import sys import sys
import qubes import qubes
import qubes.exc
import qubes.utils import qubes.utils
BLKSIZE = 512 BLKSIZE = 512
@ -214,22 +215,22 @@ class VMStorage(object):
def verify_files(self): def verify_files(self):
if not os.path.exists(self.vm.dir_path): 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)) 'VM directory does not exist: {}'.format(self.vm.dir_path))
if hasattr(self.vm, 'root_img') and not os.path.exists(self.root_img): 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)) 'VM root image file does not exist: {}'.format(self.root_img))
if hasattr(self.vm, 'private_img') \ if hasattr(self.vm, 'private_img') \
and not os.path.exists(self.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( 'VM private image file does not exist: {}'.format(
self.private_img)) self.private_img))
if self.modules_img is not None \ if self.modules_img is not None \
and not os.path.exists(self.modules_img): 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( 'VM kernel modules image does not exists: {}'.format(
self.modules_img)) self.modules_img))

View File

@ -25,6 +25,7 @@
import unittest import unittest
import qubes import qubes
import qubes.exc
import qubes.vm.adminvm import qubes.vm.adminvm
import qubes.tests import qubes.tests
@ -98,10 +99,10 @@ class TC_00_AdminVM(qubes.tests.QubesTestCase):
self.assertEqual(self.vm.get_private_img_sz(), 0) self.assertEqual(self.vm.get_private_img_sz(), 0)
def test_310_start(self): def test_310_start(self):
with self.assertRaises(qubes.QubesException): with self.assertRaises(qubes.exc.QubesException):
self.vm.start() self.vm.start()
@unittest.skip('this functionality is undecided') @unittest.skip('this functionality is undecided')
def test_311_suspend(self): def test_311_suspend(self):
with self.assertRaises(qubes.QubesException): with self.assertRaises(qubes.exc.QubesException):
self.vm.suspend() self.vm.suspend()

View File

@ -26,6 +26,7 @@
import unittest import unittest
import qubes import qubes
import qubes.exc
import qubes.config import qubes.config
import qubes.vm.qubesvm import qubes.vm.qubesvm
@ -89,13 +90,13 @@ class TC_00_setters(qubes.tests.QubesTestCase):
def test_014_setter_name_running(self): def test_014_setter_name_running(self):
self.vm.running = True 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') qubes.vm.qubesvm._setter_name(self.vm, self.prop, 'testname')
def test_015_setter_name_installed_by_rpm(self): def test_015_setter_name_installed_by_rpm(self):
# pylint: disable=invalid-name # pylint: disable=invalid-name
self.vm.installed_by_rpm = True 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') qubes.vm.qubesvm._setter_name(self.vm, self.prop, 'testname')

View File

@ -26,7 +26,7 @@
import sys import sys
import qubes import qubes.exc
import qubes.tools import qubes.tools
parser = qubes.tools.QubesArgumentParser( parser = qubes.tools.QubesArgumentParser(
@ -45,7 +45,7 @@ def main(args=None):
try: try:
args.vm.force_shutdown() args.vm.force_shutdown()
except (IOError, OSError, qubes.QubesException) as e: except (IOError, OSError, qubes.exc.QubesException) as e:
parser.error_runtime(str(e)) parser.error_runtime(str(e))
return True return True

View File

@ -131,11 +131,7 @@ def main(args=None):
preparing_dvm=args.preparing_dvm, preparing_dvm=args.preparing_dvm,
start_guid=args.start_guid) start_guid=args.start_guid)
# notify_function= # notify_function=
except MemoryError: except qubes.exc.QubesException as e:
# TODO tray
parser.error_runtime(
'not enough memory to start domain {!r}'.format(vm.name))
except qubes.QubesException as e:
parser.error_runtime('Qubes error: {!r}'.format(e)) parser.error_runtime('Qubes error: {!r}'.format(e))
return True return True

View File

@ -32,7 +32,7 @@ import docutils
import docutils.core import docutils.core
import docutils.io import docutils.io
import qubes import qubes.exc
def get_timezone(): def get_timezone():
@ -103,4 +103,4 @@ def parse_size(size):
size = size[:-len(unit)].strip() size = size[:-len(unit)].strip()
return int(size)*multiplier return int(size)*multiplier
raise qubes.QubesException("Invalid size: {0}.".format(size)) raise qubes.exc.QubesException("Invalid size: {0}.".format(size))

View File

@ -291,8 +291,7 @@ class BaseVM(qubes.PropertyHolder):
dev_match = re.match(r'([0-9a-f]+):([0-9a-f]+)\.([0-9a-f]+)', address) dev_match = re.match(r'([0-9a-f]+):([0-9a-f]+)\.([0-9a-f]+)', address)
if not dev_match: if not dev_match:
raise qubes.QubesException( raise ValueError('Invalid PCI device address: {!r}'.format(address))
'Invalid PCI device address: {}'.format(address))
hostdev = lxml.etree.Element('hostdev', type='pci', managed='yes') hostdev = lxml.etree.Element('hostdev', type='pci', managed='yes')
source = lxml.etree.Element('source') source = lxml.etree.Element('source')

View File

@ -25,6 +25,7 @@
# #
import qubes import qubes
import qubes.exc
import qubes.vm.qubesvm import qubes.vm.qubesvm
class AdminVM(qubes.vm.qubesvm.QubesVM): class AdminVM(qubes.vm.qubesvm.QubesVM):
@ -150,7 +151,7 @@ class AdminVM(qubes.vm.qubesvm.QubesVM):
.. seealso: .. seealso:
:py:meth:`qubes.vm.qubesvm.QubesVM.start` :py:meth:`qubes.vm.qubesvm.QubesVM.start`
''' # pylint: disable=unused-argument ''' # pylint: disable=unused-argument
raise qubes.QubesException('Cannot start Dom0 fake domain!') raise qubes.exc.QubesVMError('Cannot start Dom0 fake domain!')
def suspend(self): def suspend(self):

View File

@ -42,8 +42,7 @@ import libvirt
import qubes import qubes
import qubes.config import qubes.config
#import qubes.qmemman import qubes.exc
#import qubes.qmemman_algo
import qubes.storage import qubes.storage
import qubes.utils import qubes.utils
import qubes.vm import qubes.vm
@ -78,12 +77,13 @@ def _setter_name(self, prop, value):
raise ValueError('{} value contains illegal characters'.format( raise ValueError('{} value contains illegal characters'.format(
prop.__name__)) prop.__name__))
if self.is_running(): 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: try:
if self.installed_by_rpm: if self.installed_by_rpm:
raise qubes.QubesException('Cannot rename VM installed by RPM -- ' raise qubes.exc.QubesException('Cannot rename VM installed by RPM '
'first clone VM and then use yum to remove package.') '-- first clone VM and then use yum to remove package.')
except AttributeError: except AttributeError:
pass pass
@ -97,10 +97,11 @@ def _setter_kernel(self, prop, value):
qubes.config.system_path['qubes_kernels_base_dir'], qubes.config.system_path['qubes_kernels_base_dir'],
value) value)
if not os.path.exists(dirname): 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'): for filename in ('vmlinuz', 'modules.img'):
if not os.path.exists(os.path.join(dirname, filename)): 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( 'Kernel {!r} not properly installed: missing {!r} file'.format(
value, filename)) value, filename))
return value return value
@ -557,8 +558,9 @@ class QubesVM(qubes.vm.BaseVM):
# pylint: disable=unused-argument # pylint: disable=unused-argument
if self.is_running() and new_netvm is not None \ if self.is_running() and new_netvm is not None \
and not new_netvm.is_running(): and not new_netvm.is_running():
raise qubes.QubesException( raise qubes.exc.QubesVMNotStartedError(new_netvm,
'Cannot dynamically attach to stopped NetVM') 'Cannot dynamically attach to stopped NetVM: {!r}'.format(
new_netvm))
if self.netvm is not None: if self.netvm is not None:
del self.netvm.connected_vms[self] del self.netvm.connected_vms[self]
@ -601,7 +603,8 @@ class QubesVM(qubes.vm.BaseVM):
# pylint: disable=unused-argument # pylint: disable=unused-argument
# TODO not self.is_stopped() would be more appropriate # TODO not self.is_stopped() would be more appropriate
if self.is_running(): 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') @qubes.events.handler('property-set:name')
@ -640,7 +643,7 @@ class QubesVM(qubes.vm.BaseVM):
if subprocess.call(['sudo', 'systemctl', if subprocess.call(['sudo', 'systemctl',
('enable' if value else 'disable'), ('enable' if value else 'disable'),
'qubes-vm@{}.service'.format(self.name)]): 'qubes-vm@{}.service'.format(self.name)]):
raise qubes.QubesException( raise qubes.exc.QubesException(
'Failed to set autostart for VM via systemctl') '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): def on_device_pre_attached_pci(self, event, pci):
# pylint: disable=unused-argument # pylint: disable=unused-argument
if not os.path.exists('/sys/bus/pci/devices/0000:{}'.format(pci)): 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(): if not self.is_running():
return return
@ -707,7 +710,7 @@ class QubesVM(qubes.vm.BaseVM):
# Intentionally not used is_running(): eliminate also "Paused", # Intentionally not used is_running(): eliminate also "Paused",
# "Crashed", "Halting" # "Crashed", "Halting"
if self.get_power_state() != 'Halted': 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)) 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)) raise IOError('Failed to connect to qmemman: {!s}'.format(e))
if not got_memory: if not got_memory:
qmemman_client.close() qmemman_client.close()
raise MemoryError( raise qubes.exc.QubesMemoryError(self)
'Insufficient memory to start VM {!r}'.format(self.name))
# Bind pci devices to pciback driver # Bind pci devices to pciback driver
for pci in self.devices['pci']: for pci in self.devices['pci']:
@ -794,11 +796,12 @@ class QubesVM(qubes.vm.BaseVM):
def shutdown(self): def shutdown(self):
'''Shutdown domain. '''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(): if not self.is_running(): # TODO not self.is_halted()
raise qubes.QubesException("VM already stopped!") raise qubes.exc.QubesVMNotStartedError(self)
self.libvirt_domain.shutdown() self.libvirt_domain.shutdown()
@ -806,11 +809,12 @@ class QubesVM(qubes.vm.BaseVM):
def force_shutdown(self): def force_shutdown(self):
'''Forcefuly shutdown (destroy) domain. '''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(): 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() self.libvirt_domain.destroy()
@ -818,15 +822,19 @@ class QubesVM(qubes.vm.BaseVM):
def suspend(self): def suspend(self):
'''Suspend (pause) domain. '''Suspend (pause) domain.
:raises qubes.QubesException: when domain is already shut down. :raises qubes.exc.QubesVMNotRunnignError: \
:raises NotImplemetedError: when domain has PCI devices attached. 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(): 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: 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: else:
self.libvirt_domain.suspend() self.libvirt_domain.suspend()
@ -836,7 +844,7 @@ class QubesVM(qubes.vm.BaseVM):
:py:meth:`suspend`.''' :py:meth:`suspend`.'''
if not self.is_running(): if not self.is_running():
raise qubes.QubesException('VM not running!') raise qubes.exc.QubesVMNotRunningError(self)
self.suspend() self.suspend()
@ -844,18 +852,21 @@ class QubesVM(qubes.vm.BaseVM):
def resume(self): def resume(self):
'''Resume suspended domain. '''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": if self.get_power_state() == "Suspended":
raise NotImplementedError() raise qubes.exc.QubesVMError(self,
'Cannot resume suspended domain {!r}'.format(self.name))
else: else:
self.unpause() self.unpause()
def unpause(self): def unpause(self):
'''Resume (unpause) a domain''' '''Resume (unpause) a domain'''
if not self.is_paused(): if not self.is_paused():
raise qubes.QubesException('VM not paused!') raise qubes.exc.QubesVMNotPausedError(self)
self.libvirt_domain.resume() self.libvirt_domain.resume()
@ -891,28 +902,21 @@ class QubesVM(qubes.vm.BaseVM):
null = None null = None
if not self.is_running() and not self.is_paused(): if not self.is_running() and not self.is_paused():
if not autostart: if not autostart:
raise qubes.QubesException('VM not running') raise qubes.exc.QubesVMNotRunningError(self)
try: if notify_function is not None:
if notify_function is not None: notify_function('info',
notify_function('info', 'Starting the {!r} VM...'.format(self.name))
'Starting the {!r} VM...'.format(self.name)) self.start(start_guid=gui, notify_function=notify_function)
self.start(start_guid=gui, notify_function=notify_function)
except (IOError, OSError, 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 self.is_paused(): 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(): if not self.is_qrexec_running():
raise qubes.QubesException( raise qubes.exc.QubesVMError(
"Domain '{}': qrexec not connected.".format(self.name)) self, 'Domain {!r}: qrexec not connected'.format(self.name))
if gui and os.getenv("DISPLAY") is not None \ if gui and os.getenv("DISPLAY") is not None \
and not self.is_guid_running(): and not self.is_guid_running():
@ -1018,7 +1022,8 @@ class QubesVM(qubes.vm.BaseVM):
retcode = subprocess.call(guid_cmd) retcode = subprocess.call(guid_cmd)
if retcode != 0: 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') self.log.info('Sending monitor layout')
@ -1123,8 +1128,8 @@ class QubesVM(qubes.vm.BaseVM):
def resize_private_img(self, size): def resize_private_img(self, size):
'''Resize private image.''' '''Resize private image.'''
# TODO QubesValueError, not assert if size >= self.get_private_img_sz():
assert size >= self.get_private_img_sz(), "Cannot shrink private.img" raise qubes.exc.QubesValueError('Cannot shrink private.img')
# resize the image # resize the image
self.storage.resize_private_img(size) self.storage.resize_private_img(size)
@ -1139,8 +1144,9 @@ class QubesVM(qubes.vm.BaseVM):
sleep 0.2; sleep 0.2;
done; done;
resize2fs /dev/xvdb'''.format(size), user="root", wait=True) resize2fs /dev/xvdb'''.format(size), user="root", wait=True)
if retcode != 0: if retcode != 0:
raise qubes.QubesException('resize2fs failed') raise qubes.exc.QubesException('resize2fs failed')
def remove_from_disk(self): def remove_from_disk(self):
@ -1155,8 +1161,9 @@ class QubesVM(qubes.vm.BaseVM):
:param qubes.vm.qubesvm.QubesVM src: source VM :param qubes.vm.qubesvm.QubesVM src: source VM
''' '''
if src.is_running(): if src.is_running(): # XXX what about paused?
raise qubes.QubesException('Attempt to clone a running VM!') raise qubes.exc.QubesVMNotHaltedError(
self, 'Cannot clone a running domain {!r}'.format(self.name))
self.storage.clone_disk_files(src, verbose=False) 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.''' '''Attach network in this machine to it's netvm.'''
if not self.is_running(): if not self.is_running():
raise qubes.QubesException('VM not running!') raise qubes.exc.QubesVMNotRunningError(self)
assert self.netvm is not None
if self.netvm is None:
raise qubes.QubesException('NetVM not set!')
if not self.netvm.is_running(): if not self.netvm.is_running():
self.log.info('Starting NetVM ({0})'.format(self.netvm.name)) 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''' '''Detach machine from it's netvm'''
if not self.is_running(): if not self.is_running():
raise qubes.QubesException('VM not running!') raise qubes.exc.QubesVMNotRunningError(self)
assert self.netvm is not None
if self.netvm is None:
raise qubes.QubesException('NetVM not set!')
self.libvirt_domain.detachDevice(lxml.etree.ElementTree( self.libvirt_domain.detachDevice(lxml.etree.ElementTree(
self.lvxml_net_dev(self.ip, self.mac, self.netvm)).tostring()) 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( if not os.path.exists(
os.path.join(self.storage.kernels_dir, 'vmlinuz')): os.path.join(self.storage.kernels_dir, 'vmlinuz')):
raise qubes.QubesException('VM kernel does not exist: {0}'.format( raise qubes.exc.QubesException(
os.path.join(self.storage.kernels_dir, 'vmlinuz'))) 'VM kernel does not exist: {0}'.format(
os.path.join(self.storage.kernels_dir, 'vmlinuz')))
if not os.path.exists( if not os.path.exists(
os.path.join(self.storage.kernels_dir, 'initramfs')): os.path.join(self.storage.kernels_dir, 'initramfs')):
raise qubes.QubesException( raise qubes.exc.QubesException(
'VM initramfs does not exist: {0}'.format( 'VM initramfs does not exist: {0}'.format(
os.path.join(self.storage.kernels_dir, 'initramfs'))) os.path.join(self.storage.kernels_dir, 'initramfs')))

View File

@ -199,6 +199,7 @@ fi
%{python_sitelib}/qubes/config.py* %{python_sitelib}/qubes/config.py*
%{python_sitelib}/qubes/dochelpers.py* %{python_sitelib}/qubes/dochelpers.py*
%{python_sitelib}/qubes/events.py* %{python_sitelib}/qubes/events.py*
%{python_sitelib}/qubes/exc.py*
%{python_sitelib}/qubes/log.py* %{python_sitelib}/qubes/log.py*
%{python_sitelib}/qubes/plugins.py* %{python_sitelib}/qubes/plugins.py*
%{python_sitelib}/qubes/rngdoc.py* %{python_sitelib}/qubes/rngdoc.py*