From 006e7a974c5959ac58b2bb2eaed70d2cbd1319ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Thu, 14 Jan 2021 06:18:02 +0100 Subject: [PATCH 1/2] tests: fix cleanup after failed test run 'set -e' prevented the cleanup to be called. Register cleanup with 'trap' instead. Fixes: 76cd08af "Fix test suite" --- run-tests | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/run-tests b/run-tests index 59c96eb48..97119696e 100755 --- a/run-tests +++ b/run-tests @@ -11,11 +11,15 @@ install_rpm_deps () { : # we don’t actually care if this succeeds } if { command -pv rpm && command -pv dnf; }>/dev/null; then install_rpm_deps; fi -CLEANUP_LVM= + +cleanup_lvm() { + sudo --non-interactive $(dirname "$0")/ci/lvm-manage cleanup-lvm "$DEFAULT_LVM_POOL" +} + name=$(dirname "$0") if sudo --non-interactive "$name/ci/lvm-manage" setup-lvm vg$$/pool; then export DEFAULT_LVM_POOL=vg$$/pool - CLEANUP_LVM=yes + trap cleanup_lvm EXIT fi : "${PYTHON:=python3}" @@ -30,8 +34,3 @@ export PYTHONPATH "${PYTHON}" setup.py egg_info --egg-base "${TESTPYTHONPATH}" "${PYTHON}" -m coverage run --rcfile=ci/coveragerc -m qubes.tests.run "$@" -retcode=$? -if [ -n "$CLEANUP_LVM" ]; then - sudo --non-interactive $(dirname "$0")/ci/lvm-manage cleanup-lvm "$DEFAULT_LVM_POOL" -fi -exit $retcode From 1c7364e954322dfd7473b6106e893e1a5927bf21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Thu, 14 Jan 2021 06:19:28 +0100 Subject: [PATCH 2/2] storage/lvm: do not activate unused LVM volumes This prevents udev from accessing not currently used volumes, and also makes LVM snapshot operations faster (less locking to do). There is at least one unsolved problem here: inactive volumes do not have size listed on `lvs` output. There is a band-aid by caching the usage from the last time the volume was active, but it isn't enough for many cases (at the very least - reporting disk usage of a VM that wasn't started recently). There may be also other places that assume existing volumes always have relevant /dev/ node present. Fixes QubesOS/qubes-issues#6184 --- qubes/storage/lvm.py | 106 ++++++++++++++++++++++++------------- qubes/tests/storage_lvm.py | 93 +++++++++++++++++++------------- 2 files changed, 125 insertions(+), 74 deletions(-) diff --git a/qubes/storage/lvm.py b/qubes/storage/lvm.py index 99a89f477..0f17a08fc 100644 --- a/qubes/storage/lvm.py +++ b/qubes/storage/lvm.py @@ -223,16 +223,21 @@ def _parse_lvm_cache(lvm_output): line = line.decode().strip() pool_name, pool_lv, name, size, usage_percent, attr, \ origin, metadata_size, metadata_percent = line.split(';', 8) - if '' in [pool_name, name, size, usage_percent]: + if '' in [pool_name, name, size]: continue name = pool_name + "/" + name size = int(size[:-1]) # Remove 'B' suffix - usage = int(size / 100 * float(usage_percent)) + if usage_percent: + usage = int(size / 100 * float(usage_percent)) + else: + # keep cached usage for non-active volumes + usage = size_cache.get(name, {}).get('usage') if metadata_size: metadata_size = int(metadata_size[:-1]) + if metadata_percent: metadata_usage = int(metadata_size / 100 * float(metadata_percent)) else: - metadata_usage = None + metadata_usage = size_cache.get(name, {}).get('metadata_usage') result[name] = {'size': size, 'usage': usage, 'pool_lv': pool_lv, 'attr': attr, 'origin': origin, 'metadata_size': metadata_size, 'metadata_usage': metadata_usage} @@ -277,6 +282,7 @@ def init_cache_coro(log=logging.getLogger('qubes.storage.lvm')): return _parse_lvm_cache(out) size_cache_time = 0 +size_cache = {} size_cache = init_cache() @@ -357,7 +363,7 @@ def size(self, _): @asyncio.coroutine def _reset(self): - ''' Resets a volatile volume ''' + """ Resets a volatile volume. Leave it in an active state """ assert not self.snap_on_start and not self.save_on_stop, \ "Not a volatile volume" self.log.debug('Resetting volatile %s', self.vid) @@ -367,7 +373,7 @@ def _reset(self): except qubes.storage.StoragePoolException: pass # pylint: disable=protected-access - cmd = ['create', self.pool._pool_id, self.vid.split('/')[1], + cmd = ['create-activate', self.pool._pool_id, self.vid.split('/')[1], str(self.size)] yield from qubes_lvm_coro(cmd, self.log) @@ -420,7 +426,7 @@ def _commit(self, vid_to_commit=None, keep=False): vid_to_commit = self._vid_snap assert self._lock.locked() - if not os.path.exists('/dev/' + vid_to_commit): + if vid_to_commit not in size_cache: # nothing to commit return @@ -466,21 +472,21 @@ def create(self): def remove(self): assert self.vid try: - if os.path.exists('/dev/' + self._vid_snap): + if self._vid_snap in size_cache: cmd = ['remove', self._vid_snap] yield from qubes_lvm_coro(cmd, self.log) except AttributeError: pass try: - if os.path.exists('/dev/' + self._vid_import): + if self._vid_import in size_cache: cmd = ['remove', self._vid_import] yield from qubes_lvm_coro(cmd, self.log) except AttributeError: pass yield from self._remove_revisions(self.revisions.keys()) - if not os.path.exists(self.path): + if self._vid_current not in size_cache: return cmd = ['remove', self.path] yield from qubes_lvm_coro(cmd, self.log) @@ -491,10 +497,22 @@ def remove(self): def export(self): ''' Returns an object that can be `open()`. ''' # make sure the device node is available - qubes_lvm(['activate', self.path], self.log) + qubes_lvm(['activate', self._vid_current], self.log) devpath = self.path return devpath + def export_end(self, path): + ''' Returns an object that can be `open()`. ''' + # release device node + assert path == '/dev/' + self._vid_current + try: + qubes_lvm(['deactivate', path[len('/dev/'):]], self.log) + except subprocess.CalledProcessError as e: + self.log.warning('Cannot deactivate %s - failed with code %d', + self._vid_current, e.returncode) + # device may be in use by another export + pass + @qubes.storage.Volume.locked @asyncio.coroutine def import_volume(self, src_volume): @@ -513,11 +531,12 @@ def import_volume(self, src_volume): src_volume.pool.thin_pool == self.pool.thin_pool: # NOQA yield from self._commit(src_volume.path[len('/dev/'):], keep=True) else: - cmd = ['create', + cmd = ['create-activate', self.pool._pool_id, # pylint: disable=protected-access self._vid_import.split('/')[1], str(src_volume.size)] yield from qubes_lvm_coro(cmd, self.log) + yield from reset_cache_coro() src_path = yield from qubes.utils.coro_maybe(src_volume.export()) try: cmd = ['dd', 'if=' + src_path, 'of=/dev/' + self._vid_import, @@ -525,7 +544,7 @@ def import_volume(self, src_volume): if not os.access('/dev/' + self._vid_import, os.W_OK) or \ not os.access(src_path, os.R_OK): cmd.insert(0, 'sudo') - + print(cmd) p = yield from asyncio.create_subprocess_exec(*cmd) yield from p.wait() finally: @@ -551,7 +570,7 @@ def import_data(self, size): format(self.vid)) self.abort_if_import_in_progress() # pylint: disable=protected-access - cmd = ['create', self.pool._pool_id, self._vid_import.split('/')[1], + cmd = ['create-activate', self.pool._pool_id, self._vid_import.split('/')[1], str(size)] yield from qubes_lvm_coro(cmd, self.log) yield from reset_cache_coro() @@ -562,7 +581,9 @@ def import_data(self, size): @asyncio.coroutine def import_data_end(self, success): '''Either commit imported data, or discard temporary volume''' - if not os.path.exists('/dev/' + self._vid_import): + print(self._vid_import) + print(list(size_cache.keys())) + if self._vid_import not in size_cache: raise qubes.storage.StoragePoolException( 'No import operation in progress on {}'.format(self.vid)) if success: @@ -570,11 +591,11 @@ def import_data_end(self, success): else: cmd = ['remove', self._vid_import] yield from qubes_lvm_coro(cmd, self.log) + yield from reset_cache_coro() def abort_if_import_in_progress(self): try: - devpath = '/dev/' + self._vid_import - if os.path.exists(devpath): + if self._vid_import in size_cache: raise qubes.storage.StoragePoolException( 'Import operation in progress on {}'.format(self.vid)) except AttributeError: # self._vid_import @@ -583,7 +604,9 @@ def abort_if_import_in_progress(self): def is_dirty(self): if self.save_on_stop: - return os.path.exists('/dev/' + self._vid_snap) + print('is_dirty({}): {}'.format(self._vid_snap, list(size_cache.keys()))) + subprocess.check_output(['sudo', 'lvs']) + return self._vid_snap in size_cache return False def is_outdated(self): @@ -605,9 +628,9 @@ def revert(self, revision=None): if revision is None: revision = \ max(self.revisions.items(), key=_revision_sort_key)[0] - old_path = '/dev/' + self.vid + '-' + revision - if not os.path.exists(old_path): - msg = "Volume {!s} has no {!s}".format(self, old_path) + old_vid = self.vid + '-' + revision + if old_vid not in size_cache: + msg = "Volume {!s} has no {!s}".format(self, old_vid) raise qubes.storage.StoragePoolException(msg) if self.vid in size_cache: @@ -640,21 +663,30 @@ def resize(self, size): return if self.is_dirty(): + print('dirty') cmd = ['extend', self._vid_snap, str(size)] yield from qubes_lvm_coro(cmd, self.log) elif hasattr(self, '_vid_import') and \ - os.path.exists('/dev/' + self._vid_import): + self._vid_import in size_cache: + print('in import') cmd = ['extend', self._vid_import, str(size)] yield from qubes_lvm_coro(cmd, self.log) elif self.save_on_stop and not self.snap_on_start: + print('normal') cmd = ['extend', self._vid_current, str(size)] yield from qubes_lvm_coro(cmd, self.log) + print('done') self._size = size yield from reset_cache_coro() @asyncio.coroutine def _snapshot(self): + """ + Make an *active* snapshot of the source volume. + + :return: + """ try: cmd = ['remove', self._vid_snap] yield from qubes_lvm_coro(cmd, self.log) @@ -662,9 +694,9 @@ def _snapshot(self): pass if self.source is None: - cmd = ['clone', self._vid_current, self._vid_snap] + cmd = ['clone-activate', self._vid_current, self._vid_snap] else: - cmd = ['clone', self.source.path, self._vid_snap] + cmd = ['clone-activate', self.source.path, self._vid_snap] yield from qubes_lvm_coro(cmd, self.log) @qubes.storage.Volume.locked @@ -675,6 +707,9 @@ def start(self): if self.snap_on_start or self.save_on_stop: if not self.save_on_stop or not self.is_dirty(): yield from self._snapshot() + else: + cmd = ['activate', self._vid_snap] + yield from qubes_lvm_coro(cmd, self.log) else: yield from self._reset() finally: @@ -706,12 +741,7 @@ def verify(self): vid = self.source.path[len('/dev/'):] else: vid = self._vid_current - try: - vol_info = size_cache[vid] - if vol_info['attr'][4] != 'a': - raise qubes.storage.StoragePoolException( - 'volume {} not active'.format(vid)) - except KeyError: + if vid not in size_cache: raise qubes.storage.StoragePoolException( 'volume {} missing'.format(vid)) return True @@ -755,25 +785,28 @@ def _get_lvm_cmdline(cmd): :return array of str appropriate for subprocess.Popen ''' action = cmd[0] + activate = '-ay' if '-activate' in action else '-an' if action == 'remove': lvm_cmd = ['lvremove', '-f', cmd[1]] - elif action == 'clone': - lvm_cmd = ['lvcreate', '-kn', '-ay', '-s', cmd[1], '-n', cmd[2]] - elif action == 'create': - lvm_cmd = ['lvcreate', '-T', cmd[1], '-kn', '-ay', '-n', cmd[2], '-V', - str(cmd[3]) + 'B'] + elif action in ('clone', 'clone-activate'): + lvm_cmd = ['lvcreate', '-K', '-ky', activate, '-s', cmd[1], '-n', cmd[2]] + elif action in ('create', 'create-activate'): + lvm_cmd = ['lvcreate', '-T', cmd[1], '-K', '-ky', activate, '-n', cmd[2], + '-V', str(cmd[3]) + 'B'] elif action == 'extend': size = int(cmd[2]) / (1024 * 1024) lvm_cmd = ["lvextend", "-L%s" % size, cmd[1]] elif action == 'activate': - lvm_cmd = ['lvchange', '-ay', cmd[1]] + lvm_cmd = ['lvchange', '-K', '-ay', cmd[1]] + elif action == 'deactivate': + lvm_cmd = ['lvchange', '-an', cmd[1]] elif action == 'rename': lvm_cmd = ['lvrename', cmd[1], cmd[2]] else: raise NotImplementedError('unsupported action: ' + action) if lvm_is_very_old: # old lvm in trusty image used there does not support -k option - lvm_cmd = [x for x in lvm_cmd if x != '-kn'] + lvm_cmd = [x for x in lvm_cmd if x not in ('-kn', '-ky')] if os.getuid() != 0: cmd = ['sudo', 'lvm'] + lvm_cmd else: @@ -825,6 +858,7 @@ def qubes_lvm_coro(cmd, log=logging.getLogger('qubes.storage.lvm')): close_fds=True, env=environ) _, _ = yield from p.communicate() cmd = _get_lvm_cmdline(cmd) + print('calling {}'.format(' '.join(cmd))) p = yield from asyncio.create_subprocess_exec(*cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, diff --git a/qubes/tests/storage_lvm.py b/qubes/tests/storage_lvm.py index 9f2dea069..5ddd5ce49 100644 --- a/qubes/tests/storage_lvm.py +++ b/qubes/tests/storage_lvm.py @@ -103,6 +103,8 @@ def cleanup_test_volumes(self): 'sudo', 'lvremove', '-f', '/'.join([self.pool.volume_group, volume]) )) self.loop.run_until_complete(p.wait()) + qubes.storage.lvm.reset_cache() + def tearDown(self): ''' Remove the default lvm pool if it was created only for this test ''' @@ -155,6 +157,22 @@ def tearDown(self): if isinstance(getattr(self, attr), qubes.vm.BaseVM): delattr(self, attr) + def assertVolumeExists(self, vid): + try: + subprocess.check_call(['sudo', 'lvs', '--noheadings', vid], + stdout=subprocess.PIPE) + except subprocess.CalledProcessError: + self.fail('volume {} does not exist'.format(vid)) + + def assertVolumeNotExists(self, vid): + try: + subprocess.check_call(['sudo', 'lvs', '--noheadings', vid], + stdout=subprocess.PIPE) + except subprocess.CalledProcessError: + pass + else: + self.fail('volume {} does exist'.format(vid)) + def test_000_default_thin_pool(self): ''' Check whether :py:data`DEFAULT_LVM_POOL` exists. This pool is created by default, if at installation time LVM + Thin was chosen. @@ -178,8 +196,7 @@ def test_001_origin_volume(self): self.assertEqual(volume.pool, self.pool.name) self.assertEqual(volume.size, qubes.config.defaults['root_img_size']) self.loop.run_until_complete(volume.create()) - path = "/dev/%s" % volume.vid - self.assertTrue(os.path.exists(path), path) + self.assertVolumeExists(volume.vid) self.loop.run_until_complete(volume.remove()) def test_003_read_write_volume(self): @@ -198,8 +215,7 @@ def test_003_read_write_volume(self): self.assertEqual(volume.pool, self.pool.name) self.assertEqual(volume.size, qubes.config.defaults['root_img_size']) self.loop.run_until_complete(volume.create()) - path = "/dev/%s" % volume.vid - self.assertTrue(os.path.exists(path), path) + self.assertVolumeExists(volume.vid) self.loop.run_until_complete(volume.remove()) def test_004_size(self): @@ -228,15 +244,12 @@ def test_005_usage(self): self.assertEqual(usage, int(pool_size * pool_usage / 100)) def _get_size(self, path): - if os.getuid() != 0: - return int( - subprocess.check_output( - ['sudo', 'blockdev', '--getsize64', path])) - fd = os.open(path, os.O_RDONLY) - try: - return os.lseek(fd, 0, os.SEEK_END) - finally: - os.close(fd) + subprocess.check_call( + ['sudo', 'lvs', '-o', 'name,size', path]) + return int( + subprocess.check_output( + ['sudo', 'lvs', '--noheadings', '--nosuffix', + '--units', 'b', '-o', 'size', path])) def test_006_resize(self): config = { @@ -256,6 +269,7 @@ def test_006_resize(self): self.assertEqual(self._get_size(path), new_size) self.assertEqual(volume.size, new_size) + @qubes.tests.wait_on_fail def test_007_resize_running(self): old_size = 32 * 1024**2 config = { @@ -314,6 +328,7 @@ def test_008_commit(self): volume = self.app.get_pool(self.pool.name).init_volume(vm, config) self.loop.run_until_complete(volume.create()) path_snap = '/dev/' + volume._vid_snap + self.assertVolumeNotExists(volume._vid_snap) self.assertFalse(os.path.exists(path_snap), path_snap) origin_uuid = self._get_lv_uuid(volume.path) self.loop.run_until_complete(volume.start()) @@ -322,7 +337,7 @@ def test_008_commit(self): path = volume.path self.assertTrue(path.startswith('/dev/' + volume.vid), '{} does not start with /dev/{}'.format(path, volume.vid)) - self.assertTrue(os.path.exists(path), path) + self.assertVolumeExists(path) self.loop.run_until_complete(volume.remove()) def test_009_interrupted_commit(self): @@ -376,8 +391,7 @@ def test_009_interrupted_commit(self): self.assertEqual(volume.revisions, expected_revisions) self.assertEqual(volume.path, '/dev/' + volume.vid) self.assertEqual(snap_uuid, self._get_lv_uuid(volume.path)) - self.assertFalse(os.path.exists(path_snap), path_snap) - + self.assertVolumeNotExists(path_snap) self.loop.run_until_complete(volume.remove()) def test_010_migration1(self): @@ -404,7 +418,7 @@ def test_010_migration1(self): orig_uuids[rev] = self._get_lv_uuid(volume.vid + rev) qubes.storage.lvm.reset_cache() path_snap = '/dev/' + volume._vid_snap - self.assertFalse(os.path.exists(path_snap), path_snap) + self.assertVolumeNotExists(path_snap) expected_revisions = { revisions[1].lstrip('-'): '2018-03-14T22:18:24', revisions[2].lstrip('-'): '2018-03-14T22:18:25', @@ -419,7 +433,7 @@ def test_010_migration1(self): self.assertEqual(orig_uuids[''], snap_origin_uuid) path = volume.path self.assertEqual(path, '/dev/' + volume.vid) - self.assertTrue(os.path.exists(path), path) + self.assertVolumeExists(path) with unittest.mock.patch('time.time') as mock_time: mock_time.side_effect = ('1521065906', '1521065907') @@ -433,7 +447,8 @@ def test_010_migration1(self): self.assertEqual(volume.path, '/dev/' + volume.vid) path_snap = '/dev/' + volume._vid_snap self.assertFalse(os.path.exists(path_snap), path_snap) - self.assertTrue(os.path.exists('/dev/' + volume.vid)) + self.assertVolumeNotExists(path_snap) + self.assertVolumeExists(volume.vid) self.assertEqual(self._get_lv_uuid(volume.path), snap_uuid) prev_path = '/dev/' + volume.vid + revisions[3] self.assertEqual(self._get_lv_uuid(prev_path), orig_uuids['']) @@ -466,8 +481,7 @@ def test_011_migration2(self): qubes_lvm(cmd) orig_uuids[rev] = self._get_lv_uuid(volume.vid + rev) qubes.storage.lvm.reset_cache() - path_snap = '/dev/' + volume._vid_snap - self.assertTrue(os.path.exists(path_snap), path_snap) + self.assertVolumeExists(volume._vid_snap) expected_revisions = {} self.assertEqual(volume.revisions, expected_revisions) self.assertEqual(volume.path, '/dev/' + volume.vid) @@ -475,7 +489,7 @@ def test_011_migration2(self): path = volume.path self.assertEqual(path, '/dev/' + volume.vid) - self.assertTrue(os.path.exists(path), path) + self.assertVolumeExists(path) with unittest.mock.patch('time.time') as mock_time: mock_time.side_effect = ('1521065906', '1521065907') @@ -486,9 +500,8 @@ def test_011_migration2(self): } self.assertEqual(volume.revisions, expected_revisions) self.assertEqual(volume.path, '/dev/' + volume.vid) - path_snap = '/dev/' + volume._vid_snap - self.assertFalse(os.path.exists(path_snap), path_snap) - self.assertTrue(os.path.exists('/dev/' + volume.vid)) + self.assertVolumeNotExists(volume._vid_snap) + self.assertVolumeExists(volume.vid) self.assertEqual(self._get_lv_uuid(volume.path), orig_uuids['-snap']) prev_path = '/dev/' + volume.vid + revisions[2] self.assertEqual(self._get_lv_uuid(prev_path), orig_uuids['']) @@ -496,7 +509,8 @@ def test_011_migration2(self): self.loop.run_until_complete(volume.remove()) for rev in revisions: path = '/dev/' + volume.vid + rev - self.assertFalse(os.path.exists(path), path) + print(path, rev) + self.assertVolumeNotExists(path) def test_012_migration3(self): '''VM started with old code, started again with new, stopped with new''' @@ -521,8 +535,7 @@ def test_012_migration3(self): qubes_lvm(cmd) orig_uuids[rev] = self._get_lv_uuid(volume.vid + rev) qubes.storage.lvm.reset_cache() - path_snap = '/dev/' + volume._vid_snap - self.assertTrue(os.path.exists(path_snap), path_snap) + self.assertVolumeExists(volume._vid_snap) expected_revisions = {} self.assertEqual(volume.revisions, expected_revisions) self.assertTrue(volume.path, '/dev/' + volume.vid) @@ -539,6 +552,7 @@ def test_012_migration3(self): for rev in revisions: path = '/dev/' + volume.vid + rev self.assertFalse(os.path.exists(path), path) + self.assertVolumeNotExists(path) def test_013_migration4(self): '''revisions_to_keep=0, VM started with old code, stopped with new''' @@ -563,8 +577,7 @@ def test_013_migration4(self): qubes_lvm(cmd) orig_uuids[rev] = self._get_lv_uuid(volume.vid + rev) qubes.storage.lvm.reset_cache() - path_snap = '/dev/' + volume._vid_snap - self.assertTrue(os.path.exists(path_snap), path_snap) + self.assertVolumeExists(volume._vid_snap) expected_revisions = {} self.assertEqual(volume.revisions, expected_revisions) self.assertEqual(volume.path, '/dev/' + volume.vid) @@ -580,7 +593,7 @@ def test_013_migration4(self): self.loop.run_until_complete(volume.remove()) for rev in revisions: path = '/dev/' + volume.vid + rev - self.assertFalse(os.path.exists(path), path) + self.assertVolumeNotExists(path) def test_014_commit_keep_0(self): ''' Test volume changes commit, with revisions_to_keep=0''' @@ -693,8 +706,7 @@ def test_021_revert_earlier(self): self.assertFalse(volume.is_dirty()) self.assertNotEqual(current_uuid, rev_uuid) self.loop.run_until_complete(volume.revert(revision_id)) - path_snap = '/dev/' + volume._vid_snap - self.assertFalse(os.path.exists(path_snap), path_snap) + self.assertVolumeNotExists(volume._vid_snap) self.assertEqual(current_path, volume.path) new_uuid = self._get_lv_origin_uuid(volume.path) self.assertEqual(new_uuid, rev_uuid) @@ -730,7 +742,7 @@ def test_030_import_data(self): revision = revisions.popitem()[0] self.assertEqual(current_uuid, self._get_lv_uuid(volume.vid + '-' + revision)) - self.assertFalse(os.path.exists(import_path), import_path) + self.assertVolumeNotExists(import_path) self.loop.run_until_complete(volume.remove()) @@ -759,7 +771,7 @@ def test_031_import_data_fail(self): self.assertEqual(new_current_uuid, current_uuid) revisions = volume.revisions self.assertEqual(len(revisions), 0) - self.assertFalse(os.path.exists(import_path), import_path) + self.assertVolumeNotExists(import_path) self.loop.run_until_complete(volume.remove()) @@ -888,7 +900,7 @@ def test_034_import_data_empty(self): 'sudo', 'touch', import_path)) self.loop.run_until_complete(p.wait()) self.loop.run_until_complete(volume.import_data_end(True)) - self.assertFalse(os.path.exists(import_path), import_path) + self.assertVolumeNotExists(import_path) p = self.loop.run_until_complete(asyncio.create_subprocess_exec( 'sudo', 'cat', volume.path, stdout=subprocess.PIPE @@ -937,15 +949,19 @@ def test_040_volatile(self): path = volume.path self.assertEqual(path, '/dev/' + volume.vid) self.assertFalse(os.path.exists(path)) + self.assertVolumeNotExists(path) self.loop.run_until_complete(volume.start()) self.assertTrue(os.path.exists(path)) + self.assertVolumeExists(path) vol_uuid = self._get_lv_uuid(path) self.loop.run_until_complete(volume.start()) self.assertTrue(os.path.exists(path)) + self.assertVolumeExists(path) vol_uuid2 = self._get_lv_uuid(path) self.assertNotEqual(vol_uuid, vol_uuid2) self.loop.run_until_complete(volume.stop()) self.assertFalse(os.path.exists(path)) + self.assertVolumeNotExists(path) def test_050_snapshot_volume(self): ''' Test snapshot volume creation ''' @@ -981,6 +997,7 @@ def test_050_snapshot_volume(self): path = volume.path self.assertEqual(path, '/dev/' + volume.vid) self.assertFalse(os.path.exists(path), path) + self.assertVolumeNotExists(path) self.loop.run_until_complete(volume.start()) # snapshot volume isn't considered dirty at any time self.assertFalse(volume.is_dirty()) @@ -1001,9 +1018,9 @@ def test_050_snapshot_volume(self): # stopped volume is never outdated self.assertFalse(volume.is_outdated()) path = volume.path - self.assertFalse(os.path.exists(path), path) + self.assertVolumeNotExists(path) path = '/dev/' + volume._vid_snap - self.assertFalse(os.path.exists(path), path) + self.assertVolumeNotExists(path) self.loop.run_until_complete(volume.remove()) self.loop.run_until_complete(volume_origin.remove())