Skip to content

Commit e76372b

Browse files
committed
storage: add size and usage properties to pool object
Add Pool.size and Pool.usage to the API. Implement them for LVM and File pools. Add appropriate tests. QubesOS/qubes-issues#3240
1 parent 682d950 commit e76372b

File tree

5 files changed

+73
-1
lines changed

5 files changed

+73
-1
lines changed

qubes/storage/__init__.py

+10
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,16 @@ def get_volume(self, vid):
815815
'''
816816
raise self._not_implemented("get_volume")
817817

818+
@property
819+
def size(self):
820+
''' Storage pool size in bytes '''
821+
raise self._not_implemented("size")
822+
823+
@property
824+
def usage(self):
825+
''' Space used in the pool, in bytes '''
826+
raise self._not_implemented("usage")
827+
818828
def _not_implemented(self, method_name):
819829
''' Helper for emitting helpful `NotImplementedError` exceptions '''
820830
msg = "Pool driver {!s} has {!s}() not implemented"

qubes/storage/file.py

+10
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ def target_dir(self, vm):
144144
def list_volumes(self):
145145
return self._volumes
146146

147+
@property
148+
def size(self):
149+
statvfs = os.statvfs(self.dir_path)
150+
return statvfs.f_frsize * statvfs.f_blocks
151+
152+
@property
153+
def usage(self):
154+
statvfs = os.statvfs(self.dir_path)
155+
return statvfs.f_frsize * (statvfs.f_blocks - statvfs.f_bfree)
156+
147157

148158
class FileVolume(qubes.storage.Volume):
149159
''' Parent class for the xen volumes implementation which expects a

qubes/storage/lvm.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,22 @@ def list_volumes(self):
137137
volumes += [ThinVolume(**config)]
138138
return volumes
139139

140+
@property
141+
def size(self):
142+
try:
143+
return qubes.storage.lvm.size_cache[
144+
self.volume_group + '/' + self.thin_pool]['size']
145+
except KeyError:
146+
return 0
147+
148+
@property
149+
def usage(self):
150+
try:
151+
return qubes.storage.lvm.size_cache[
152+
self.volume_group + '/' + self.thin_pool]['usage']
153+
except KeyError:
154+
return 0
155+
140156

141157
def init_cache(log=logging.getLogger('qubes.storage.lvm')):
142158
cmd = ['lvs', '--noheadings', '-o',
@@ -159,7 +175,7 @@ def init_cache(log=logging.getLogger('qubes.storage.lvm')):
159175
line = line.decode().strip()
160176
pool_name, pool_lv, name, size, usage_percent, attr, \
161177
origin = line.split(';', 6)
162-
if '' in [pool_name, pool_lv, name, size, usage_percent]:
178+
if '' in [pool_name, name, size, usage_percent]:
163179
continue
164180
name = pool_name + "/" + name
165181
size = int(size[:-1]) # Remove 'B' suffix

qubes/tests/storage_file.py

+15
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,21 @@ def test_002_pool_dir_create(self):
369369

370370
shutil.rmtree(pool_dir, ignore_errors=True)
371371

372+
def test_003_size(self):
373+
pool = self.app.get_pool(self.POOL_NAME)
374+
with self.assertNotRaises(NotImplementedError):
375+
size = pool.size
376+
statvfs = os.statvfs(self.POOL_DIR)
377+
self.assertEqual(size, statvfs.f_blocks * statvfs.f_frsize)
378+
379+
def test_004_usage(self):
380+
pool = self.app.get_pool(self.POOL_NAME)
381+
with self.assertNotRaises(NotImplementedError):
382+
usage = pool.usage
383+
statvfs = os.statvfs(self.POOL_DIR)
384+
self.assertEqual(usage,
385+
statvfs.f_frsize * (statvfs.f_blocks - statvfs.f_bfree))
386+
372387
def test_011_appvm_file_images(self):
373388
""" Check if all the needed image files are created for an AppVm"""
374389

qubes/tests/storage_lvm.py

+21
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
'''
2727

2828
import os
29+
import subprocess
2930
import unittest
3031

3132
import qubes.tests
@@ -158,6 +159,26 @@ def test_003_read_write_volume(self):
158159
self.assertTrue(os.path.exists(path))
159160
volume.remove()
160161

162+
def test_004_size(self):
163+
with self.assertNotRaises(NotImplementedError):
164+
size = self.pool.size
165+
pool_size = subprocess.check_output(['sudo', 'lvs', '--noheadings',
166+
'-o', 'lv_size',
167+
'--units', 'b', self.pool.volume_group + '/' + self.pool.thin_pool])
168+
self.assertEqual(size, int(pool_size.strip()[:-1]))
169+
170+
def test_005_usage(self):
171+
with self.assertNotRaises(NotImplementedError):
172+
usage = self.pool.usage
173+
pool_info = subprocess.check_output(['sudo', 'lvs', '--noheadings',
174+
'-o', 'lv_size,data_percent',
175+
'--units', 'b', self.pool.volume_group + '/' + self.pool.thin_pool])
176+
pool_size, pool_usage = pool_info.strip().split()
177+
pool_size = int(pool_size[:-1])
178+
pool_usage = float(pool_usage)
179+
self.assertEqual(usage, int(pool_size * pool_usage / 100))
180+
181+
161182
@skipUnlessLvmPoolExists
162183
class TC_01_ThinPool(ThinPoolBase, qubes.tests.SystemTestCase):
163184
''' Sanity tests for :py:class:`qubes.storage.lvm.ThinPool` '''

0 commit comments

Comments
 (0)