diff --git a/qubes/device_protocol.py b/qubes/device_protocol.py index e84af6d13..fe78ffa00 100644 --- a/qubes/device_protocol.py +++ b/qubes/device_protocol.py @@ -38,7 +38,7 @@ from typing import TYPE_CHECKING import qubes.utils -from qubes.exc import ProtocolError, QubesValueError +from qubes.exc import ProtocolError, QubesValueError, UnexpectedDeviceProperty if TYPE_CHECKING: from qubes.vm.qubesvm import QubesVM @@ -46,12 +46,6 @@ QubesVM = "qubes.vm.qubesvm.QubesVM" -class UnexpectedDeviceProperty(qubes.exc.QubesException, ValueError): - """ - Device has unexpected property such as backend_domain, devclass etc. - """ - - def qbool(value): return qubes.property.bool(None, None, value) diff --git a/qubes/devices.py b/qubes/devices.py index 69745b410..e494432d5 100644 --- a/qubes/devices.py +++ b/qubes/devices.py @@ -71,31 +71,12 @@ VirtualDevice, AssignmentMode, ) -from qubes.exc import ProtocolError - - -class DeviceNotAssigned(qubes.exc.QubesException, KeyError): - """ - Trying to unassign not assigned device. - """ - - -class DeviceAlreadyAttached(qubes.exc.QubesException, KeyError): - """ - Trying to attach already attached device. - """ - - -class DeviceAlreadyAssigned(qubes.exc.QubesException, KeyError): - """ - Trying to assign already assigned device. - """ - - -class UnrecognizedDevice(qubes.exc.QubesException, ValueError): - """ - Device identity is not as expected. - """ +from qubes.exc import ( + ProtocolError, + DeviceNotAssigned, + DeviceAlreadyAttached, + DeviceAlreadyAssigned, +) class DeviceCollection: diff --git a/qubes/exc.py b/qubes/exc.py index f0b6322e3..93dc2b5e7 100644 --- a/qubes/exc.py +++ b/qubes/exc.py @@ -269,3 +269,37 @@ class ProtocolError(AssertionError): class PermissionDenied(Exception): """Raised deliberately by handlers when we decide not to cooperate""" + + +class DeviceNotAssigned(QubesException, KeyError): + """ + Trying to unassign not assigned device. + """ + + +class DeviceAlreadyAttached(QubesException, KeyError): + """ + Trying to attach already attached device. + """ + + +class DeviceAlreadyAssigned(QubesException, KeyError): + """ + Trying to assign already assigned device. + """ + + +class UnrecognizedDevice(QubesException, ValueError): + """ + Device identity is not as expected. + """ + + +class UnexpectedDeviceProperty(QubesException, ValueError): + """ + Device has unexpected property such as backend_domain, devclass etc. + """ + + +class StoragePoolException(QubesException): + """A general storage exception""" diff --git a/qubes/ext/block.py b/qubes/ext/block.py index 244e62371..e77957644 100644 --- a/qubes/ext/block.py +++ b/qubes/ext/block.py @@ -30,6 +30,7 @@ import qubes.device_protocol import qubes.devices +import qubes.exc import qubes.ext from qubes.ext import utils from qubes.storage import Storage @@ -514,7 +515,7 @@ def pre_attachment_internal( "not available, skipping.", file=sys.stderr, ) - raise qubes.devices.UnrecognizedDevice() + raise qubes.exc.UnrecognizedDevice() # validate options for option, value in options.items(): @@ -561,7 +562,7 @@ def pre_attachment_internal( return if device.attachment and device.attachment != expected_attachment: - raise qubes.devices.DeviceAlreadyAttached( + raise qubes.exc.DeviceAlreadyAttached( "Device {!s} already attached to {!s}".format( device, device.attachment ) diff --git a/qubes/storage/__init__.py b/qubes/storage/__init__.py index 5577728b4..bfef07eeb 100644 --- a/qubes/storage/__init__.py +++ b/qubes/storage/__init__.py @@ -38,6 +38,7 @@ import qubes import qubes.exc import qubes.utils +from qubes.exc import StoragePoolException STORAGE_ENTRY_POINT = "qubes.storage" _am_root = os.getuid() == 0 @@ -46,10 +47,6 @@ _big_buffer = b"\0" * BYTES_TO_ZERO -class StoragePoolException(qubes.exc.QubesException): - """A general storage exception""" - - class BlockDevice: """Represents a storage block device.""" diff --git a/qubes/storage/callback.py b/qubes/storage/callback.py index 8b3a7a864..286132f75 100644 --- a/qubes/storage/callback.py +++ b/qubes/storage/callback.py @@ -25,12 +25,14 @@ import asyncio import locale from shlex import quote + +import qubes.exc from qubes.utils import coro_maybe import qubes.storage -class UnhandledSignalException(qubes.storage.StoragePoolException): +class UnhandledSignalException(qubes.exc.StoragePoolException): def __init__(self, pool, signal): super().__init__( "The pool %s failed to handle the signal %s, likely because it was run from synchronous code." @@ -204,7 +206,7 @@ def __init__(self, *, name, conf_id): "qubes.storage.callback" ) #: Logger instance. if not isinstance(conf_id, str): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "conf_id is no String. VM attack?!" ) self._cb_conf_id = ( @@ -215,7 +217,7 @@ def __init__(self, *, name, conf_id): with open(config_path, encoding="utf-8") as json_file: conf_all = json.load(json_file) if not isinstance(conf_all, dict): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "The file %s is supposed to define a dict." % config_path ) @@ -225,7 +227,7 @@ def __init__(self, *, name, conf_id): ] #: Dictionary holding all configuration for the given _cb_conf_id. except KeyError: # we cannot throw KeyErrors as we'll otherwise generate incorrect error messages @qubes.app._get_pool() - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "The specified conf_id %s could not be found inside %s." % (self._cb_conf_id, config_path) ) @@ -233,7 +235,7 @@ def __init__(self, *, name, conf_id): try: bdriver = self._cb_conf["bdriver"] except KeyError: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Missing bdriver for the conf_id %s inside %s." % (self._cb_conf_id, config_path) ) @@ -247,12 +249,12 @@ def __init__(self, *, name, conf_id): qubes.storage.STORAGE_ENTRY_POINT, bdriver ) except KeyError: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "The driver %s was not found on your system." % bdriver ) if not issubclass(cls, qubes.storage.Pool): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "The class %s must be a subclass of qubes.storage.Pool." % cls ) diff --git a/qubes/storage/file.py b/qubes/storage/file.py index d47ff7392..8b0bd727f 100644 --- a/qubes/storage/file.py +++ b/qubes/storage/file.py @@ -30,6 +30,7 @@ import subprocess from contextlib import suppress +import qubes.exc import qubes.storage import qubes.utils @@ -350,7 +351,7 @@ def resize(self, size): # pylint: disable=invalid-overridden-method """ if not self.rw: msg = "Can not resize readonly volume {!s}".format(self) - raise qubes.storage.StoragePoolException(msg) + raise qubes.exc.StoragePoolException(msg) if self.snap_on_start: # this theoretically could be supported, but it's unusual @@ -359,10 +360,10 @@ def resize(self, size): # pylint: disable=invalid-overridden-method "Cannot resize volume based on a template - resize" "the template instead" ) - raise qubes.storage.StoragePoolException(msg) + raise qubes.exc.StoragePoolException(msg) if size < self.size: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "For your own safety, shrinking of %s is" " disabled. If you really know what you" " are doing, use `truncate` on %s manually." @@ -407,11 +408,11 @@ def export(self): # pylint: disable=invalid-overridden-method assert ( self._export_lock is FileVolume._marker_running ), "nested calls to export()" - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "file pool cannot export running volumes" ) if self.is_dirty(): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "file pool cannot export dirty volumes" ) self._export_lock = FileVolume._marker_exported @@ -425,7 +426,7 @@ async def export_end(self, path): async def import_volume(self, src_volume): if src_volume.snap_on_start: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Can not import snapshot volume {!s} in to pool {!s} ".format( src_volume, self ) @@ -441,7 +442,7 @@ async def import_volume(self, src_volume): def import_data(self, size): # pylint: disable=invalid-overridden-method if not self.save_on_stop: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Can not import into save_on_stop=False volume {!s}".format( self ) @@ -476,7 +477,7 @@ async def start(self): assert ( self._export_lock is FileVolume._marker_exported ), "nested calls to start()" - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "file pool cannot start a VM with an exported volume" ) self._export_lock = FileVolume._marker_running @@ -559,7 +560,7 @@ def verify(self): # pylint: disable=invalid-overridden-method self.snap_on_start or self.save_on_stop ): msg = "Missing image file: {!s}.".format(self.path) - raise qubes.storage.StoragePoolException(msg) + raise qubes.exc.StoragePoolException(msg) return True @property @@ -722,4 +723,4 @@ def _check_path(path): """Raise an StoragePoolException if ``path`` does not exist""" if not os.path.exists(path): msg = "Missing image file: %s" % path - raise qubes.storage.StoragePoolException(msg) + raise qubes.exc.StoragePoolException(msg) diff --git a/qubes/storage/kernels.py b/qubes/storage/kernels.py index d8c6b7145..b413dfc86 100644 --- a/qubes/storage/kernels.py +++ b/qubes/storage/kernels.py @@ -25,7 +25,8 @@ import qubes.exc import qubes.storage -from qubes.storage import Pool, StoragePoolException, Volume +from qubes.storage import Pool, Volume +from qubes.exc import StoragePoolException class LinuxModules(Volume): diff --git a/qubes/storage/lvm.py b/qubes/storage/lvm.py index dcd9e8a54..d0b4a0cef 100644 --- a/qubes/storage/lvm.py +++ b/qubes/storage/lvm.py @@ -27,6 +27,7 @@ import asyncio import qubes +import qubes.exc import qubes.storage import qubes.utils import json @@ -146,11 +147,11 @@ async def setup(self): await reset_cache_coro() cache_key = self.volume_group + "/" + self.thin_pool if cache_key not in size_cache: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Thin pool {} does not exist".format(cache_key) ) if size_cache[cache_key]["attr"][0] != "t": - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Volume {} is not a thin pool".format(cache_key) ) # TODO Should we create a non existing pool? @@ -299,7 +300,7 @@ def init_cache(log=logging.getLogger("qubes.storage.lvm")): if return_code == 0 and err: log.warning(err) elif return_code != 0: - raise qubes.storage.StoragePoolException(err) + raise qubes.exc.StoragePoolException(err) return _parse_lvm_cache(out) @@ -319,7 +320,7 @@ async def init_cache_coro(log=logging.getLogger("qubes.storage.lvm")): if return_code == 0 and err: log.warning(err) elif return_code != 0: - raise qubes.storage.StoragePoolException(err) + raise qubes.exc.StoragePoolException(err) return _parse_lvm_cache(out) @@ -413,7 +414,7 @@ async def _reset(self): try: cmd = ["remove", self._vid_current] await qubes_lvm_coro(cmd, self.log) - except qubes.storage.StoragePoolException: + except qubes.exc.StoragePoolException: pass # pylint: disable=protected-access cmd = [ @@ -444,7 +445,7 @@ async def _remove_revisions(self, revisions=None): try: cmd = ["remove", self.vid + "-" + rev_id] await qubes_lvm_coro(cmd, self.log) - except qubes.storage.StoragePoolException: + except qubes.exc.StoragePoolException: pass async def _activate(self): @@ -564,7 +565,7 @@ async def import_volume(self, src_volume): return self if self.is_dirty(): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Cannot import to dirty volume {} -" " start and stop a qube to cleanup".format(self.vid) ) @@ -610,7 +611,7 @@ async def import_volume(self, src_volume): if p.returncode != 0: cmd = ["remove", self._vid_import] await qubes_lvm_coro(cmd, self.log) - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Failed to import volume {!r}, dd exit code: {}".format( src_volume, p.returncode ) @@ -624,7 +625,7 @@ async def import_volume(self, src_volume): async def import_data(self, size): """Returns an object that can be `open()`.""" if self.is_dirty(): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Cannot import data to dirty volume {}," " stop the qube first".format(self.vid) ) @@ -645,7 +646,7 @@ async def import_data(self, size): async def import_data_end(self, success): """Either commit imported data, or discard temporary volume""" if not os.path.exists("/dev/" + self._vid_import): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "No import operation in progress on {}".format(self.vid) ) if success: @@ -659,7 +660,7 @@ async def import_data_end(self, success): def abort_if_import_in_progress(self): try: if self._vid_import in size_cache: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Import operation in progress on {}".format(self.vid) ) except AttributeError: # self._vid_import @@ -685,7 +686,7 @@ def is_outdated(self): @qubes.storage.Volume.locked async def revert(self, revision=None): if self.is_dirty(): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Cannot revert dirty volume {}, stop the qube first".format( self.vid ) @@ -696,7 +697,7 @@ async def revert(self, revision=None): old_path = "/dev/" + self.vid + "-" + revision if not os.path.exists(old_path): msg = "Volume {!s} has no {!s}".format(self, old_path) - raise qubes.storage.StoragePoolException(msg) + raise qubes.exc.StoragePoolException(msg) if self.vid in size_cache: cmd = ["remove", self.vid] @@ -714,10 +715,10 @@ async def resize(self, size): """ if not self.rw: msg = "Can not resize readonly volume {!s}".format(self) - raise qubes.storage.StoragePoolException(msg) + raise qubes.exc.StoragePoolException(msg) if size < self.size: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "For your own safety, shrinking of %s is" " disabled (%d < %d). If you really know what you" " are doing, use `lvresize` on %s manually." @@ -817,7 +818,7 @@ async def verify(self): try: size_cache[vid] except KeyError: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "volume {} missing".format(vid) ) return True @@ -938,7 +939,7 @@ def _process_lvm_output(returncode, stdout, stderr, log): elif returncode != 0: assert err, "Command exited unsuccessful, but printed nothing to stderr" err = err.replace("%", "%%") - raise qubes.storage.StoragePoolException(err) + raise qubes.exc.StoragePoolException(err) return True diff --git a/qubes/storage/reflink.py b/qubes/storage/reflink.py index 42e325dc0..92470cd5d 100644 --- a/qubes/storage/reflink.py +++ b/qubes/storage/reflink.py @@ -35,6 +35,7 @@ import tempfile from contextlib import contextmanager, suppress +import qubes.exc import qubes.storage import qubes.utils @@ -94,7 +95,7 @@ def setup(self): # pylint: disable=invalid-overridden-method if self._setup_check and not is_supported(self.dir_path): if created: _remove_empty_dir(self.dir_path) - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "The filesystem for {!r} does not support reflinks. If you" " can live with VM startup delays and wasted disk space, pass" ' the "setup_check=False" option.'.format(self.dir_path) @@ -220,7 +221,7 @@ def verify(self): # pylint: disable=invalid-overridden-method if img is None or os.path.exists(img): return True - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Missing image file {!r} for volume {}".format(img, self.vid) ) @@ -323,7 +324,7 @@ def revert( self, revision=None ): # pylint: disable=invalid-overridden-method if self.is_dirty(): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Cannot revert: {} is not cleanly stopped".format(self.vid) ) path_revision = self._path_revision(revision) @@ -339,7 +340,7 @@ def resize(self, size): # pylint: disable=invalid-overridden-method devices of the size change. """ if not self.rw: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Cannot resize: {} is read-only".format(self.vid) ) try: @@ -556,7 +557,7 @@ def _copy_file(src, dst, *, dst_size=None, copy_mtime=False): check=False, ) if result.returncode != 0: - raise qubes.storage.StoragePoolException(str(result)) + raise qubes.exc.StoragePoolException(str(result)) if dst_size is not None: tmp_io.truncate(dst_size) if copy_mtime: diff --git a/qubes/storage/zfs.py b/qubes/storage/zfs.py index a100d17fd..00e9eac73 100644 --- a/qubes/storage/zfs.py +++ b/qubes/storage/zfs.py @@ -15,6 +15,7 @@ import time import qubes +import qubes.exc import qubes.storage import qubes.storage.file import qubes.utils @@ -73,7 +74,7 @@ async def fail_unless_exists_async(path: str) -> None: if os.path.exists(path): return err = f"Device path {path} never appeared" - raise qubes.storage.StoragePoolException(err) + raise qubes.exc.StoragePoolException(err) def get_random_string( @@ -107,7 +108,7 @@ async def retry_async( async def wait_for_device_async(devpath: str) -> str: await retry_async( lambda: fail_unless_exists_async(devpath), - qubes.storage.StoragePoolException, + qubes.exc.StoragePoolException, 1000, 0.01, ) @@ -205,19 +206,19 @@ async def duplicate_disk( p = await asyncio.create_subprocess_exec(*thecmd) ret = await p.wait() if ret != 0: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "%s failed with error %s" % (thecmd, ret) ) def check_zfs_available() -> None: if not shutil.which(_zfs) or not shutil.which(_zpool): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "ZFS is not available on this system", ) -class DatasetBusy(qubes.storage.StoragePoolException): +class DatasetBusy(qubes.exc.StoragePoolException): """ Dataset is busy. Causes: @@ -226,7 +227,7 @@ class DatasetBusy(qubes.storage.StoragePoolException): """ -class DatasetHasDependentClones(qubes.storage.StoragePoolException): +class DatasetHasDependentClones(qubes.exc.StoragePoolException): """ Dataset has dependent clones. @@ -240,7 +241,7 @@ class DatasetHasDependentClones(qubes.storage.StoragePoolException): """ -class DatasetDoesNotExist(qubes.storage.StoragePoolException): +class DatasetDoesNotExist(qubes.exc.StoragePoolException): """ Dataset does not exist. @@ -255,7 +256,7 @@ def _enoent_is_spe(): yield except FileNotFoundError as exc: # Oops. No ZFS. Raise the appropriate exception. - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "ZFS is not available on this system", ) from exc @@ -370,7 +371,7 @@ def _process_zfs_output( raise DatasetHasDependentClones(err) if err.rstrip().endswith("dataset does not exist"): raise DatasetDoesNotExist(err) - raise qubes.storage.StoragePoolException(err) + raise qubes.exc.StoragePoolException(err) return stdout.decode() @@ -632,7 +633,7 @@ async def __init_container(self) -> None: self.container, log=self.log, ) - except qubes.storage.StoragePoolException: + except qubes.exc.StoragePoolException: self.log.info("Creating container dataset %s", self.container) await zfs_async( "create", @@ -666,7 +667,7 @@ async def destroy(self) -> None: self.container, log=self.log, ) - except qubes.storage.StoragePoolException: + except qubes.exc.StoragePoolException: # Pool container does not exist anymore. return @@ -1051,7 +1052,7 @@ async def volume_exists_async( self._cache.set(vol, "readonly", rdnly) drty = row["org.qubes:dirty"] == "on" self._cache.set(vol, "org.qubes:dirty", drty) - except qubes.storage.StoragePoolException: + except qubes.exc.StoragePoolException: pass self._initialized = True @@ -1062,7 +1063,7 @@ async def volume_exists_async( await self._get_prop_async(volume, "name", log=log) self._ack_exists_nl(volume) return True - except qubes.storage.StoragePoolException: + except qubes.exc.StoragePoolException: self._cache.invalidate_recursively(volume) self._cache.set(volume, "exists", False) return False @@ -1106,7 +1107,7 @@ def volume_exists( self._cache.set(vol, "readonly", rdnly) drty = row["org.qubes:dirty"] == "on" self._cache.set(vol, "org.qubes:dirty", drty) - except qubes.storage.StoragePoolException: + except qubes.exc.StoragePoolException: pass self._initialized = True @@ -1117,7 +1118,7 @@ def volume_exists( self._get_prop(volume, "name", log=log) self._ack_exists_nl(volume) return True - except qubes.storage.StoragePoolException: + except qubes.exc.StoragePoolException: self._cache.invalidate_recursively(volume) self._cache.set(volume, "exists", False) return False @@ -1834,7 +1835,7 @@ def __init__( """ if snap_on_start and save_on_stop: err = "ZFSVolume %s cannot be snap_on_start && save_on_stop" % vid - raise qubes.storage.StoragePoolException(err) + raise qubes.exc.StoragePoolException(err) # Intify. # The code is now designed to work with zero # revisions_to_keep. @@ -1857,7 +1858,7 @@ def __init__( if DEBUG_IS_WARNING: self.log.debug = self.log.warning # type:ignore if kwargs: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Unsupported arguments received: %s" % ", ".join(kwargs), ) self._auto_snapshot_policy = ( @@ -2573,7 +2574,7 @@ async def import_volume( ) if self.is_dirty(): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( f"Cannot import to dirty volume {self.volume} —" " start and stop its owning qube to clean it up" ) @@ -2590,7 +2591,7 @@ def abort_if_import_in_progress(self) -> None: self.importing_volume_name, log=self.log, ): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Import operation in progress on {}".format(self.volume) ) @@ -2603,7 +2604,7 @@ async def import_data(self, size: int) -> str: """ self.log.debug("Importing data of size %s into %s", size, self.volume) if self.is_dirty(): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Cannot import data to dirty volume {} -" " stop the qube using it first".format(self.volume) ) @@ -2679,7 +2680,7 @@ async def resize(self, size: int) -> None: if size == mysize: return if size < mysize: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Shrinking of ZFS volume %s is not possible" % (self.volume,) ) if await self.pool.accessor.volume_exists_async( @@ -2723,7 +2724,7 @@ def is_outdated(self) -> bool: # since they will be created on demand. return False if not isinstance(self.source, ZFSVolume): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "%s cannot be cloned by ZFSVolume" % self.source ) @@ -2785,7 +2786,7 @@ def latest_revision(self) -> Tuple[str, str]: """ revs = self.revisions if not revs: - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "No revisions in %s" % self.volume, ) return list( @@ -2803,7 +2804,7 @@ async def revert(self, revision: Optional[str] = None) -> "ZFSVolume": """ self.log.debug("revert %s to %s", self.volume, revision) if self.is_dirty(): - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "Cannot revert dirty volume {} -" " stop the qube first".format( self.volume, @@ -2813,7 +2814,7 @@ async def revert(self, revision: Optional[str] = None) -> "ZFSVolume": snaps = self.revisions norevs = "Cannot revert volume %s with no revisions" % self.volume if not snaps: - raise qubes.storage.StoragePoolException(norevs) + raise qubes.exc.StoragePoolException(norevs) if revision is None: snap, _ = self.latest_revision else: @@ -2845,7 +2846,7 @@ async def verify(self) -> bool: log=self.log, ): return True - raise qubes.storage.StoragePoolException( + raise qubes.exc.StoragePoolException( "volume {} missing".format( self.volume, ) diff --git a/qubes/tests/api_admin.py b/qubes/tests/api_admin.py index 89d48f9e4..a992a226c 100644 --- a/qubes/tests/api_admin.py +++ b/qubes/tests/api_admin.py @@ -2784,7 +2784,7 @@ def test_495_vm_device_unassign_not_assigned(self): with unittest.mock.patch.object( qubes.vm.qubesvm.QubesVM, "is_halted", lambda _: False ): - with self.assertRaises(qubes.devices.DeviceNotAssigned): + with self.assertRaises(qubes.exc.DeviceNotAssigned): self.call_mgmt_func( b"admin.vm.device.testclass.Detach", b"test-vm1", @@ -2827,7 +2827,7 @@ def test_497_vm_device_detach_not_attached(self): with unittest.mock.patch.object( qubes.vm.qubesvm.QubesVM, "is_halted", lambda _: False ): - with self.assertRaises(qubes.devices.DeviceNotAssigned): + with self.assertRaises(qubes.exc.DeviceNotAssigned): self.call_mgmt_func( b"admin.vm.device.testclass.Detach", b"test-vm1", diff --git a/qubes/tests/devices.py b/qubes/tests/devices.py index 13081dfe1..9484446d3 100644 --- a/qubes/tests/devices.py +++ b/qubes/tests/devices.py @@ -22,6 +22,7 @@ # import qubes.devices +import qubes.exc from qubes.device_protocol import ( Port, DeviceInfo, @@ -163,7 +164,7 @@ def test_011_empty_unassign(self): def test_012_double_attach(self): self.attach() - with self.assertRaises(qubes.devices.DeviceAlreadyAttached): + with self.assertRaises(qubes.exc.DeviceAlreadyAttached): self.loop.run_until_complete( self.collection.attach(self.assignment) ) @@ -175,7 +176,7 @@ def test_013_double_detach(self): ) self.detach() - with self.assertRaises(qubes.devices.DeviceNotAssigned): + with self.assertRaises(qubes.exc.DeviceNotAssigned): self.loop.run_until_complete( self.collection.detach(self.assignment.port) ) @@ -183,7 +184,7 @@ def test_013_double_detach(self): def test_014_double_assign(self): self.loop.run_until_complete(self.collection.assign(self.assignment)) - with self.assertRaises(qubes.devices.DeviceAlreadyAssigned): + with self.assertRaises(qubes.exc.DeviceAlreadyAssigned): self.loop.run_until_complete( self.collection.assign(self.assignment) ) @@ -192,7 +193,7 @@ def test_015_double_unassign(self): self.loop.run_until_complete(self.collection.assign(self.assignment)) self.loop.run_until_complete(self.collection.unassign(self.assignment)) - with self.assertRaises(qubes.devices.DeviceNotAssigned): + with self.assertRaises(qubes.exc.DeviceNotAssigned): self.loop.run_until_complete( self.collection.unassign(self.assignment) ) diff --git a/qubes/tests/integ/audio.py b/qubes/tests/integ/audio.py index 4c1fcbf39..d10d64b40 100644 --- a/qubes/tests/integ/audio.py +++ b/qubes/tests/integ/audio.py @@ -29,6 +29,7 @@ import numpy as np +import qubes.exc import qubes.vm import qubes.devices from qubes.tests.integ.vm_qrexec_gui import TC_00_AppVMMixin, in_qemu @@ -438,7 +439,7 @@ def common_audio_record_unmuted(self, attach_mic=True, detach_mic=True): if attach_mic: try: self.detach_mic() - except qubes.devices.DeviceNotAssigned: + except qubes.exc.DeviceNotAssigned: pass self.attach_mic() # connect VM's recording source output monitor (instead of mic) diff --git a/qubes/tests/integ/basic.py b/qubes/tests/integ/basic.py index e3b6406eb..70e0aff65 100644 --- a/qubes/tests/integ/basic.py +++ b/qubes/tests/integ/basic.py @@ -37,6 +37,7 @@ import sys import qubes +import qubes.exc import qubes.firewall import qubes.tests import qubes.storage @@ -888,7 +889,7 @@ def test_100_resize_root_img(self): ) except ( subprocess.CalledProcessError, - qubes.storage.StoragePoolException, + qubes.exc.StoragePoolException, ) as e: # exception object would leak VM reference self.fail(str(e)) @@ -922,7 +923,7 @@ def test_101_resize_root_img_online(self): ) except ( subprocess.CalledProcessError, - qubes.storage.StoragePoolException, + qubes.exc.StoragePoolException, ) as e: # exception object would leak VM reference self.fail(str(e)) diff --git a/qubes/tests/storage_callback.py b/qubes/tests/storage_callback.py index bbbe26a68..ef53f85e5 100644 --- a/qubes/tests/storage_callback.py +++ b/qubes/tests/storage_callback.py @@ -25,6 +25,8 @@ import os import json import subprocess + +import qubes.exc import qubes.tests import qubes.tests.storage import qubes.tests.storage_lvm @@ -395,21 +397,21 @@ def test_002_failing_callback(self): def test_003_errors(self): """Make sure we error out on common user & dev mistakes.""" # missing conf_id - with self.assertRaises(qubes.storage.StoragePoolException): + with self.assertRaises(qubes.exc.StoragePoolException): cb = CallbackPool(name="some-name", conf_id="") # invalid conf_id - with self.assertRaises(qubes.storage.StoragePoolException): + with self.assertRaises(qubes.exc.StoragePoolException): cb = CallbackPool(name="some-name", conf_id="nonexisting-id") # incorrect backend driver - with self.assertRaises(qubes.storage.StoragePoolException): + with self.assertRaises(qubes.exc.StoragePoolException): cb = CallbackPool( name="some-name", conf_id="testing-fail-incorrect-bdriver" ) # missing config entries - with self.assertRaises(qubes.storage.StoragePoolException): + with self.assertRaises(qubes.exc.StoragePoolException): cb = CallbackPool( name="some-name", conf_id="testing-fail-missing-all" ) diff --git a/qubes/tests/storage_zfs.py b/qubes/tests/storage_zfs.py index 28e601faa..e44fd9772 100644 --- a/qubes/tests/storage_zfs.py +++ b/qubes/tests/storage_zfs.py @@ -15,6 +15,7 @@ import tempfile import unittest +import qubes.exc import qubes.storage as storage import qubes.storage.zfs as zfs import qubes.tests @@ -237,7 +238,7 @@ def test_random_string(self): def test_fail_unless_exists_async(self): with tempfile.TemporaryDirectory() as tmpdir: nonexistent = os.path.join(tmpdir, "x") - with self.assertRaises(storage.StoragePoolException): + with self.assertRaises(qubes.exc.StoragePoolException): self.rc(zfs.fail_unless_exists_async(nonexistent)) with open(nonexistent, "w", encoding="utf-8") as f: f.write("now it exists") @@ -295,7 +296,7 @@ def test_dd(self): with open(dst, "r", encoding="utf-8") as f: self.assertEqual(f.read(), "now it exists") falsesrc = os.path.join(tmpdir, "unrelated") - with self.assertRaises(storage.StoragePoolException): + with self.assertRaises(qubes.exc.StoragePoolException): self.rc(zfs.duplicate_disk(falsesrc, dst, log)) @@ -432,7 +433,7 @@ def test_013_resize_saveonstop(self) -> None: # Fail at shrinking. self.assertRaises( - storage.StoragePoolException, + qubes.exc.StoragePoolException, lambda: self.rc(volume.resize(1024 * 1024)), ) @@ -617,7 +618,7 @@ def test_020_saveonstop_clone_from_snaponstart(self) -> None: # This should never be the case. Snap on start volumes # must instead be *sourced* from a save on stop volume. self.assertRaises( - storage.StoragePoolException, + qubes.exc.StoragePoolException, lambda: self.get_vol(ONEMEG_SNAP_ON_START, name="020-2"), ) @@ -813,9 +814,9 @@ def deinit(): with patch.object( tgt.vol.pool.accessor, "clone_snapshot_to_volume_async", - side_effect=storage.StoragePoolException("mocked failure!"), + side_effect=qubes.exc.StoragePoolException("mocked failure!"), ): - with self.assertRaises(storage.StoragePoolException): + with self.assertRaises(qubes.exc.StoragePoolException): # Fail clone! self.rc(tgt.vol.import_volume(src.vol)) diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py index 3821f9e4c..58c4cd813 100644 --- a/qubes/vm/qubesvm.py +++ b/qubes/vm/qubesvm.py @@ -1595,7 +1595,7 @@ async def on_domain_stopped(self, _event, **_kwargs): """Cleanup after domain was stopped""" try: await self.storage.stop() - except qubes.storage.StoragePoolException: + except qubes.exc.StoragePoolException: self.log.exception( "Failed to stop storage for domain %s", self.name )