Added admin.pool.UsageDetails API method
admin.pool.UsageDetails reports the usage data, unlike admin.pool.Info, which should report the config/unchangeable data. At the moment admin.Pool.Info still reports usage, to maintain compatibility, but once all relevant tools are updated, it should just return configuration data.
This commit is contained in:
parent
04a215fb83
commit
2f6497e48d
1
Makefile
1
Makefile
@ -21,6 +21,7 @@ ADMIN_API_METHODS_SIMPLE = \
|
|||||||
admin.label.Remove \
|
admin.label.Remove \
|
||||||
admin.pool.Add \
|
admin.pool.Add \
|
||||||
admin.pool.Info \
|
admin.pool.Info \
|
||||||
|
admin.pool.UsageDetails \
|
||||||
admin.pool.List \
|
admin.pool.List \
|
||||||
admin.pool.ListDrivers \
|
admin.pool.ListDrivers \
|
||||||
admin.pool.Remove \
|
admin.pool.Remove \
|
||||||
|
@ -622,6 +622,7 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
|
|||||||
self.fire_event_for_permission(pool=pool)
|
self.fire_event_for_permission(pool=pool)
|
||||||
|
|
||||||
other_info = ''
|
other_info = ''
|
||||||
|
# Deprecated: remove this when all tools using this call are updated
|
||||||
pool_size = pool.size
|
pool_size = pool.size
|
||||||
if pool_size is not None:
|
if pool_size is not None:
|
||||||
other_info += 'size={}\n'.format(pool_size)
|
other_info += 'size={}\n'.format(pool_size)
|
||||||
@ -630,11 +631,6 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
|
|||||||
if pool_usage is not None:
|
if pool_usage is not None:
|
||||||
other_info += 'usage={}\n'.format(pool_usage)
|
other_info += 'usage={}\n'.format(pool_usage)
|
||||||
|
|
||||||
pool_details = pool.usage_details
|
|
||||||
for name in pool_details:
|
|
||||||
if name not in ['data_size', 'data_usage']:
|
|
||||||
other_info += '{}={}\n'.format(name, pool_details[name])
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
included_in = pool.included_in(self.app)
|
included_in = pool.included_in(self.app)
|
||||||
if included_in:
|
if included_in:
|
||||||
@ -646,6 +642,26 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
|
|||||||
for prop, val in sorted(pool.config.items())) + \
|
for prop, val in sorted(pool.config.items())) + \
|
||||||
other_info
|
other_info
|
||||||
|
|
||||||
|
@qubes.api.method('admin.pool.UsageDetails', no_payload=True,
|
||||||
|
scope='global', read=True)
|
||||||
|
@asyncio.coroutine
|
||||||
|
def pool_usage(self):
|
||||||
|
self.enforce(self.dest.name == 'dom0')
|
||||||
|
self.enforce(self.arg in self.app.pools.keys())
|
||||||
|
|
||||||
|
pool = self.app.pools[self.arg]
|
||||||
|
|
||||||
|
self.fire_event_for_permission(pool=pool)
|
||||||
|
|
||||||
|
usage = ''
|
||||||
|
|
||||||
|
pool_details = pool.usage_details
|
||||||
|
|
||||||
|
for name in sorted(pool_details):
|
||||||
|
usage += '{}={}\n'.format(name, pool_details[name])
|
||||||
|
|
||||||
|
return usage
|
||||||
|
|
||||||
@qubes.api.method('admin.pool.Add',
|
@qubes.api.method('admin.pool.Add',
|
||||||
scope='global', write=True)
|
scope='global', write=True)
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
@ -207,8 +207,8 @@ class ThinPool(qubes.storage.Pool):
|
|||||||
metadata_usage = qubes.storage.lvm.size_cache[
|
metadata_usage = qubes.storage.lvm.size_cache[
|
||||||
self.volume_group + '/' + self.thin_pool]['metadata_usage']
|
self.volume_group + '/' + self.thin_pool]['metadata_usage']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
metadata_size = None
|
metadata_size = 0
|
||||||
metadata_usage = None
|
metadata_usage = 0
|
||||||
result['metadata_size'] = metadata_size
|
result['metadata_size'] = metadata_size
|
||||||
result['metadata_usage'] = metadata_usage
|
result['metadata_usage'] = metadata_usage
|
||||||
|
|
||||||
|
@ -582,14 +582,13 @@ class TC_00_VMs(AdminAPITestCase):
|
|||||||
'pool1': unittest.mock.Mock(config={
|
'pool1': unittest.mock.Mock(config={
|
||||||
'param1': 'value1', 'param2': 'value2'},
|
'param1': 'value1', 'param2': 'value2'},
|
||||||
usage=102400,
|
usage=102400,
|
||||||
size=204800,
|
size=204800)
|
||||||
usage_details={'metadata_size': 500})
|
|
||||||
}
|
}
|
||||||
self.app.pools['pool1'].included_in.return_value = None
|
self.app.pools['pool1'].included_in.return_value = None
|
||||||
value = self.call_mgmt_func(b'admin.pool.Info', b'dom0', b'pool1')
|
value = self.call_mgmt_func(b'admin.pool.Info', b'dom0', b'pool1')
|
||||||
|
|
||||||
self.assertEqual(value,
|
self.assertEqual(value,
|
||||||
'param1=value1\nparam2=value2\nsize=204800\nusage=102400\nmetadata_size=500\n')
|
'param1=value1\nparam2=value2\nsize=204800\nusage=102400\n')
|
||||||
self.assertFalse(self.app.save.called)
|
self.assertFalse(self.app.save.called)
|
||||||
|
|
||||||
def test_151_pool_info_unsupported_size(self):
|
def test_151_pool_info_unsupported_size(self):
|
||||||
@ -623,6 +622,23 @@ class TC_00_VMs(AdminAPITestCase):
|
|||||||
'\nincluded_in=pool1\n')
|
'\nincluded_in=pool1\n')
|
||||||
self.assertFalse(self.app.save.called)
|
self.assertFalse(self.app.save.called)
|
||||||
|
|
||||||
|
def test_153_pool_usage(self):
|
||||||
|
self.app.pools = {
|
||||||
|
'pool1': unittest.mock.Mock(config={
|
||||||
|
'param1': 'value1', 'param2': 'value2'},
|
||||||
|
usage_details={
|
||||||
|
'data_usage': 102400,
|
||||||
|
'data_size': 204800,
|
||||||
|
'metadata_size': 1024,
|
||||||
|
'metadata_usage': 50})
|
||||||
|
}
|
||||||
|
self.app.pools['pool1'].included_in.return_value = None
|
||||||
|
value = self.call_mgmt_func(b'admin.pool.UsageDetails', b'dom0', b'pool1')
|
||||||
|
|
||||||
|
self.assertEqual(value,
|
||||||
|
'data_size=204800\ndata_usage=102400\nmetadata_size=1024\nmetadata_usage=50\n')
|
||||||
|
self.assertFalse(self.app.save.called)
|
||||||
|
|
||||||
@unittest.mock.patch('qubes.storage.pool_drivers')
|
@unittest.mock.patch('qubes.storage.pool_drivers')
|
||||||
@unittest.mock.patch('qubes.storage.driver_parameters')
|
@unittest.mock.patch('qubes.storage.driver_parameters')
|
||||||
def test_160_pool_add(self, mock_parameters, mock_drivers):
|
def test_160_pool_add(self, mock_parameters, mock_drivers):
|
||||||
|
@ -997,6 +997,38 @@ class TC_00_ThinPool(ThinPoolBase):
|
|||||||
self.assertNotIn(volume1, list(self.pool.volumes))
|
self.assertNotIn(volume1, list(self.pool.volumes))
|
||||||
self.assertNotIn(volume1, list(self.pool.volumes))
|
self.assertNotIn(volume1, list(self.pool.volumes))
|
||||||
|
|
||||||
|
def test_110_metadata_size(self):
|
||||||
|
with self.assertNotRaises(NotImplementedError):
|
||||||
|
usage = self.pool.usage_details
|
||||||
|
|
||||||
|
metadata_size = usage['metadata_size']
|
||||||
|
environ = os.environ.copy()
|
||||||
|
environ['LC_ALL'] = 'C.utf8'
|
||||||
|
pool_size = subprocess.check_output(['sudo', 'lvs', '--noheadings',
|
||||||
|
'-o', 'lv_metadata_size',
|
||||||
|
'--units', 'b',
|
||||||
|
self.pool.volume_group + '/' + self.pool.thin_pool],
|
||||||
|
env=environ)
|
||||||
|
self.assertEqual(metadata_size, int(pool_size.strip()[:-1]))
|
||||||
|
|
||||||
|
def test_111_metadata_usage(self):
|
||||||
|
with self.assertNotRaises(NotImplementedError):
|
||||||
|
usage = self.pool.usage_details
|
||||||
|
|
||||||
|
metadata_usage = usage['metadata_usage']
|
||||||
|
environ = os.environ.copy()
|
||||||
|
environ['LC_ALL'] = 'C.utf8'
|
||||||
|
|
||||||
|
pool_info = subprocess.check_output(['sudo', 'lvs', '--noheadings',
|
||||||
|
'-o', 'lv_metadata_size,metadata_percent',
|
||||||
|
'--units', 'b', self.pool.volume_group + '/' + self.pool.thin_pool],
|
||||||
|
env=environ)
|
||||||
|
pool_size, pool_usage = pool_info.strip().split()
|
||||||
|
pool_size = int(pool_size[:-1])
|
||||||
|
pool_usage = float(pool_usage)
|
||||||
|
self.assertEqual(metadata_usage, int(pool_size * pool_usage / 100))
|
||||||
|
|
||||||
|
|
||||||
@skipUnlessLvmPoolExists
|
@skipUnlessLvmPoolExists
|
||||||
class TC_01_ThinPool(ThinPoolBase, qubes.tests.SystemTestCase):
|
class TC_01_ThinPool(ThinPoolBase, qubes.tests.SystemTestCase):
|
||||||
''' Sanity tests for :py:class:`qubes.storage.lvm.ThinPool` '''
|
''' Sanity tests for :py:class:`qubes.storage.lvm.ThinPool` '''
|
||||||
|
Loading…
Reference in New Issue
Block a user