Skip to content

Commit

Permalink
Add metadata info to LVM AdminAPI
Browse files Browse the repository at this point in the history
Added usage_details method to Pool class
(returns a dictionary with detailed information
on pool usage) and LVM implementation that returns
metadata info.
Needed for QubesOS/qubes-issues#5053
  • Loading branch information
marmarta committed Aug 8, 2019
1 parent 205f621 commit c7ff349
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 7 deletions.
5 changes: 5 additions & 0 deletions qubes/api/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,11 @@ def pool_info(self):
if pool_usage is not None:
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:
included_in = pool.included_in(self.app)
if included_in:
Expand Down
11 changes: 11 additions & 0 deletions qubes/storage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,17 @@ def size(self):
def usage(self):
''' Space used in the pool in bytes, or None if unknown '''

@property
def usage_details(self):
"""Detailed information about pool usage as a dictionary
Contains data_usage for usage in bytes and data_size for pool
size; other implementations may add more implementation-specific
detail"""
return {
'data_usage': self.usage,
'data_size': self.size
}

def _not_implemented(self, method_name):
''' Helper for emitting helpful `NotImplementedError` exceptions '''
msg = "Pool driver {!s} has {!s}() not implemented"
Expand Down
32 changes: 28 additions & 4 deletions qubes/storage/lvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,25 +195,49 @@ def usage(self):
except KeyError:
return 0

@property
def usage_details(self):
result = {}
result['data_size'] = self.size
result['data_usage'] = self.usage

try:
metadata_size = qubes.storage.lvm.size_cache[
self.volume_group + '/' + self.thin_pool]['metadata_size']
metadata_usage = qubes.storage.lvm.size_cache[
self.volume_group + '/' + self.thin_pool]['metadata_usage']
except KeyError:
metadata_size = None
metadata_usage = None
result['metadata_size'] = metadata_size
result['metadata_usage'] = metadata_usage

return result

_init_cache_cmd = ['lvs', '--noheadings', '-o',
'vg_name,pool_lv,name,lv_size,data_percent,lv_attr,origin',
'--units', 'b', '--separator', ';']
'vg_name,pool_lv,name,lv_size,data_percent,lv_attr,origin,lv_metadata_size,'
'metadata_percent', '--units', 'b', '--separator', ';']

def _parse_lvm_cache(lvm_output):
result = {}

for line in lvm_output.splitlines():
line = line.decode().strip()
pool_name, pool_lv, name, size, usage_percent, attr, \
origin = line.split(';', 6)
origin, metadata_size, metadata_percent = line.split(';', 8)
if '' in [pool_name, name, size, usage_percent]:
continue
name = pool_name + "/" + name
size = int(size[:-1]) # Remove 'B' suffix
usage = int(size / 100 * float(usage_percent))
if metadata_size:
metadata_size = int(metadata_size[:-1])
metadata_usage = int(metadata_size / 100 * float(metadata_percent))
else:
metadata_usage = None
result[name] = {'size': size, 'usage': usage, 'pool_lv': pool_lv,
'attr': attr, 'origin': origin}
'attr': attr, 'origin': origin, 'metadata_size': metadata_size,
'metadata_usage': metadata_usage}

return result

Expand Down
7 changes: 4 additions & 3 deletions qubes/tests/api_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -582,20 +582,21 @@ def test_150_pool_info(self):
'pool1': unittest.mock.Mock(config={
'param1': 'value1', 'param2': 'value2'},
usage=102400,
size=204800)
size=204800,
usage_details={'metadata_size': 500})
}
self.app.pools['pool1'].included_in.return_value = None
value = self.call_mgmt_func(b'admin.pool.Info', b'dom0', b'pool1')

self.assertEqual(value,
'param1=value1\nparam2=value2\nsize=204800\nusage=102400\n')
'param1=value1\nparam2=value2\nsize=204800\nusage=102400\nmetadata_size=500')
self.assertFalse(self.app.save.called)

def test_151_pool_info_unsupported_size(self):
self.app.pools = {
'pool1': unittest.mock.Mock(config={
'param1': 'value1', 'param2': 'value2'},
size=None, usage=None),
size=None, usage=None, usage_details={}),
}
self.app.pools['pool1'].included_in.return_value = None
value = self.call_mgmt_func(b'admin.pool.Info', b'dom0', b'pool1')
Expand Down

0 comments on commit c7ff349

Please sign in to comment.