Kaynağa Gözat

Add metadata info to LVM AdminAPI

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
Marta Marczykowska-Górecka 4 yıl önce
ebeveyn
işleme
04a215fb83

+ 5 - 0
qubes/api/admin.py

@@ -630,6 +630,11 @@ class QubesAdminAPI(qubes.api.AbstractQubesAPI):
         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:

+ 11 - 0
qubes/storage/__init__.py

@@ -795,6 +795,17 @@ class Pool:
     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"

+ 28 - 4
qubes/storage/lvm.py

@@ -195,10 +195,28 @@ class ThinPool(qubes.storage.Pool):
         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 = {}
@@ -206,14 +224,20 @@ def _parse_lvm_cache(lvm_output):
     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
 

+ 4 - 3
qubes/tests/api_admin.py

@@ -582,20 +582,21 @@ class TC_00_VMs(AdminAPITestCase):
             '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\n')
         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')