瀏覽代碼

qvm-template-postprocess: resize volume only when needed

If needs to be extended - do it before import. If needs to be reduced -
after. This way, if data import fails for any reason, previous data
won't be destroyed (truncated).
Also, convert error on shrinking volume to a warning, as it doesn't break
the template (just leave it with bigger disk than needed). Currently all
storage pool implementations refuse to shrink a volume (but it may
change in the future).

QubesOS/qubes-issues#3169
Marek Marczykowski-Górecki 6 年之前
父節點
當前提交
562137c36d
共有 2 個文件被更改,包括 48 次插入6 次删除
  1. 34 5
      qubesadmin/tests/tools/qvm_template_postprocess.py
  2. 14 1
      qubesadmin/tools/qvm_template_postprocess.py

+ 34 - 5
qubesadmin/tests/tools/qvm_template_postprocess.py

@@ -57,6 +57,18 @@ class TC_00_qvm_template_postprocess(qubesadmin.tests.QubesTestCase):
         self.app.expected_calls[('test-vm', 'admin.vm.volume.List', None,
                 None)] = \
             b'0\0root\nprivate\nvolatile\nkernel\n'
+        self.app.expected_calls[
+            ('test-vm', 'admin.vm.volume.Info', 'root', None)] = \
+            b'0\x00pool=lvm\n' \
+            b'vid=qubes_dom0/vm-test-vm-root\n' \
+            b'size=10737418240\n' \
+            b'usage=0\n' \
+            b'rw=True\n' \
+            b'source=\n' \
+            b'save_on_stop=True\n' \
+            b'snap_on_start=False\n' \
+            b'revisions_to_keep=3\n' \
+            b'is_outdated=False\n'
         self.app.expected_calls[('test-vm', 'admin.vm.volume.Resize', 'root',
                 str(len(volume_data)).encode())] = \
             b'0\0'
@@ -82,6 +94,18 @@ class TC_00_qvm_template_postprocess(qubesadmin.tests.QubesTestCase):
 
         self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
             b'0\0test-vm class=TemplateVM state=Halted\n'
+        self.app.expected_calls[
+            ('test-vm', 'admin.vm.volume.Info', 'root', None)] = \
+            b'0\x00pool=lvm\n' \
+            b'vid=qubes_dom0/vm-test-vm-root\n' \
+            b'size=10737418240\n' \
+            b'usage=0\n' \
+            b'rw=True\n' \
+            b'source=\n' \
+            b'save_on_stop=True\n' \
+            b'snap_on_start=False\n' \
+            b'revisions_to_keep=3\n' \
+            b'is_outdated=False\n'
         self.app.expected_calls[('test-vm', 'admin.vm.volume.List', None,
                 None)] = \
             b'0\0root\nprivate\nvolatile\nkernel\n'
@@ -114,17 +138,22 @@ class TC_00_qvm_template_postprocess(qubesadmin.tests.QubesTestCase):
             b'0\0root\nprivate\nvolatile\nkernel\n'
         self.app.expected_calls[
             ('test-vm', 'admin.vm.volume.Info', 'root', None)] = \
-            b'0\0pool=default\nvid=vm-templates/test-vm/root\n'
+            b'0\x00pool=default\n' \
+            b'vid=vm-templates/test-vm/root\n' \
+            b'size=10737418240\n' \
+            b'usage=0\n' \
+            b'rw=True\n' \
+            b'source=\n' \
+            b'save_on_stop=True\n' \
+            b'snap_on_start=False\n' \
+            b'revisions_to_keep=3\n' \
+            b'is_outdated=False\n'
         self.app.expected_calls[
             ('dom0', 'admin.pool.List', None, None)] = \
             b'0\0default\n'
         self.app.expected_calls[
             ('dom0', 'admin.pool.Info', 'default', None)] = \
             b'0\0driver=file\ndir_path=' + self.source_dir.name.encode() + b'\n'
-        self.app.expected_calls[
-            ('test-vm', 'admin.vm.volume.Resize', 'root',
-                str(len(volume_data)).encode())] = \
-            b'0\0'
 
         vm = self.app.domains['test-vm']
         qubesadmin.tools.qvm_template_postprocess.import_root_img(

+ 14 - 1
qubesadmin/tools/qvm_template_postprocess.py

@@ -77,8 +77,12 @@ def get_root_img_size(source_dir):
 def import_root_img(vm, source_dir):
     '''Import root.img into VM object'''
 
+    # Try not break existing data in the volume in case of import failure. If
+    #  volume needs to be extended, do it before import, if reduced - after.
+
     root_size = get_root_img_size(source_dir)
-    vm.volumes['root'].resize(root_size)
+    if vm.volumes['root'].size < root_size:
+        vm.volumes['root'].resize(root_size)
 
     root_path = os.path.join(source_dir, 'root.img')
     if os.path.exists(root_path + '.part.00'):
@@ -108,6 +112,15 @@ def import_root_img(vm, source_dir):
         with open(root_path, 'rb') as root_file:
             vm.volumes['root'].import_data(stream=root_file)
 
+    if vm.volumes['root'].size > root_size:
+        try:
+            vm.volumes['root'].resize(root_size)
+        except qubesadmin.exc.QubesException as err:
+            vm.log.warning(
+                'Failed to resize root volume of {} from {} to {} after '
+                'import: {}'.format(vm.name, vm.volumes['root'].size,
+                    root_size, str(err)))
+
 
 def import_appmenus(vm, source_dir):
     '''Import appmenus settings into VM object (later: GUI VM)'''