diff --git a/qubes/storage/__init__.py b/qubes/storage/__init__.py index 9dcd7c2a..e5997e7d 100644 --- a/qubes/storage/__init__.py +++ b/qubes/storage/__init__.py @@ -815,6 +815,16 @@ class Pool(object): ''' raise self._not_implemented("get_volume") + @property + def size(self): + ''' Storage pool size in bytes ''' + raise self._not_implemented("size") + + @property + def usage(self): + ''' Space used in the pool, in bytes ''' + raise self._not_implemented("usage") + def _not_implemented(self, method_name): ''' Helper for emitting helpful `NotImplementedError` exceptions ''' msg = "Pool driver {!s} has {!s}() not implemented" diff --git a/qubes/storage/file.py b/qubes/storage/file.py index 8969d591..0dfb8b8f 100644 --- a/qubes/storage/file.py +++ b/qubes/storage/file.py @@ -144,6 +144,16 @@ class FilePool(qubes.storage.Pool): def list_volumes(self): return self._volumes + @property + def size(self): + statvfs = os.statvfs(self.dir_path) + return statvfs.f_frsize * statvfs.f_blocks + + @property + def usage(self): + statvfs = os.statvfs(self.dir_path) + return statvfs.f_frsize * (statvfs.f_blocks - statvfs.f_bfree) + class FileVolume(qubes.storage.Volume): ''' Parent class for the xen volumes implementation which expects a diff --git a/qubes/storage/lvm.py b/qubes/storage/lvm.py index 107e7e37..8563416e 100644 --- a/qubes/storage/lvm.py +++ b/qubes/storage/lvm.py @@ -137,6 +137,22 @@ class ThinPool(qubes.storage.Pool): volumes += [ThinVolume(**config)] return volumes + @property + def size(self): + try: + return qubes.storage.lvm.size_cache[ + self.volume_group + '/' + self.thin_pool]['size'] + except KeyError: + return 0 + + @property + def usage(self): + try: + return qubes.storage.lvm.size_cache[ + self.volume_group + '/' + self.thin_pool]['usage'] + except KeyError: + return 0 + def init_cache(log=logging.getLogger('qubes.storage.lvm')): cmd = ['lvs', '--noheadings', '-o', @@ -159,7 +175,7 @@ def init_cache(log=logging.getLogger('qubes.storage.lvm')): line = line.decode().strip() pool_name, pool_lv, name, size, usage_percent, attr, \ origin = line.split(';', 6) - if '' in [pool_name, pool_lv, name, size, usage_percent]: + if '' in [pool_name, name, size, usage_percent]: continue name = pool_name + "/" + name size = int(size[:-1]) # Remove 'B' suffix diff --git a/qubes/tests/storage_file.py b/qubes/tests/storage_file.py index 19d46e11..94d5d679 100644 --- a/qubes/tests/storage_file.py +++ b/qubes/tests/storage_file.py @@ -369,6 +369,21 @@ class TC_03_FilePool(qubes.tests.QubesTestCase): shutil.rmtree(pool_dir, ignore_errors=True) + def test_003_size(self): + pool = self.app.get_pool(self.POOL_NAME) + with self.assertNotRaises(NotImplementedError): + size = pool.size + statvfs = os.statvfs(self.POOL_DIR) + self.assertEqual(size, statvfs.f_blocks * statvfs.f_frsize) + + def test_004_usage(self): + pool = self.app.get_pool(self.POOL_NAME) + with self.assertNotRaises(NotImplementedError): + usage = pool.usage + statvfs = os.statvfs(self.POOL_DIR) + self.assertEqual(usage, + statvfs.f_frsize * (statvfs.f_blocks - statvfs.f_bfree)) + def test_011_appvm_file_images(self): """ Check if all the needed image files are created for an AppVm""" diff --git a/qubes/tests/storage_lvm.py b/qubes/tests/storage_lvm.py index 18662b95..f6a4be69 100644 --- a/qubes/tests/storage_lvm.py +++ b/qubes/tests/storage_lvm.py @@ -26,6 +26,7 @@ ''' import os +import subprocess import unittest import qubes.tests @@ -158,6 +159,26 @@ class TC_00_ThinPool(ThinPoolBase): self.assertTrue(os.path.exists(path)) volume.remove() + def test_004_size(self): + with self.assertNotRaises(NotImplementedError): + size = self.pool.size + pool_size = subprocess.check_output(['sudo', 'lvs', '--noheadings', + '-o', 'lv_size', + '--units', 'b', self.pool.volume_group + '/' + self.pool.thin_pool]) + self.assertEqual(size, int(pool_size.strip()[:-1])) + + def test_005_usage(self): + with self.assertNotRaises(NotImplementedError): + usage = self.pool.usage + pool_info = subprocess.check_output(['sudo', 'lvs', '--noheadings', + '-o', 'lv_size,data_percent', + '--units', 'b', self.pool.volume_group + '/' + self.pool.thin_pool]) + pool_size, pool_usage = pool_info.strip().split() + pool_size = int(pool_size[:-1]) + pool_usage = float(pool_usage) + self.assertEqual(usage, int(pool_size * pool_usage / 100)) + + @skipUnlessLvmPoolExists class TC_01_ThinPool(ThinPoolBase, qubes.tests.SystemTestCase): ''' Sanity tests for :py:class:`qubes.storage.lvm.ThinPool` '''