From: Peter Krempa <[email protected]>

When taking a full backup of a VM which has potentially fully-allocated
zero blocks the output image will not be sparse. Add an option to
sparsify full backups so that they potentially skip large blocks of NUL
blocks.

For incremental backup images this is obviously not possible as any NUL
blocks were actually zeroed-out by the VM so they need to be written
as-is.

Signed-off-by: Peter Krempa <[email protected]>
---
 docs/formatbackup.rst                         |  5 +++++
 src/conf/backup_conf.c                        | 18 +++++++++++++++++-
 src/conf/backup_conf.h                        |  2 ++
 src/conf/schemas/domainbackup.rng             | 10 ++++++++++
 src/qemu/qemu_backup.c                        |  5 +++++
 .../backup-push-sparsify.xml                  | 12 ++++++++++++
 .../backup-push-sparsify.xml                  | 19 +++++++++++++++++++
 tests/genericxml2xmltest.c                    |  1 +
 8 files changed, 71 insertions(+), 1 deletion(-)
 create mode 100644 tests/domainbackupxml2xmlin/backup-push-sparsify.xml
 create mode 100644 tests/domainbackupxml2xmlout/backup-push-sparsify.xml

diff --git a/docs/formatbackup.rst b/docs/formatbackup.rst
index 24857eaf72..db7ccf802b 100644
--- a/docs/formatbackup.rst
+++ b/docs/formatbackup.rst
@@ -115,6 +115,11 @@ were supplied). The following child elements and 
attributes are supported:
          for an incremental backup exported via NBD export name for the given
          disk. Valid only for pull mode backups.

+      ``sparsify``
+         Valid only for ``full`` push mode backups.
+         Requests that any zero blocks are un-allocated in the backup file even
+         when they were allocated in the source. :since:`Since 12.9.0`
+
       ``type``
          A mandatory attribute to describe the type of the disk, except when
          ``backup='no'`` is used. Valid values include ``file``, or ``block``.
diff --git a/src/conf/backup_conf.c b/src/conf/backup_conf.c
index b20292af3d..87a5b64116 100644
--- a/src/conf/backup_conf.c
+++ b/src/conf/backup_conf.c
@@ -129,7 +129,11 @@ virDomainBackupDiskDefParseXML(xmlNodePtr node,
     if (def->backup == VIR_TRISTATE_BOOL_NO)
         return 0;

-    if (!push) {
+    if (push) {
+        if (virXMLPropTristateBool(node, "sparsify", VIR_XML_PROP_NONE,
+                                   &def->sparsify) < 0)
+            return -1;
+    } else {
         def->exportname = virXMLPropString(node, "exportname");
         def->exportbitmap = virXMLPropString(node, "exportbitmap");
     }
@@ -325,6 +329,10 @@ virDomainBackupDiskDefFormat(virBuffer *buf,
         virBufferEscapeString(&attrBuf, " exportname='%s'", disk->exportname);
         virBufferEscapeString(&attrBuf, " exportbitmap='%s'", 
disk->exportbitmap);

+        if (disk->sparsify != VIR_TRISTATE_BOOL_ABSENT)
+            virBufferAsprintf(&attrBuf, " sparsify='%s'",
+                              virTristateBoolTypeToString(disk->sparsify));
+
         if (disk->store->id != 0)
             virBufferAsprintf(&attrBuf, " index='%u'", disk->store->id);

@@ -535,6 +543,14 @@ virDomainBackupAlignDisks(virDomainBackupDef *def,
         if (!backupdisk->incremental &&
             backupdisk->backupmode == 
VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL)
             backupdisk->incremental = g_strdup(def->incremental);
+
+        if (backupdisk->sparsify == VIR_TRISTATE_BOOL_YES &&
+            backupdisk->backupmode == 
VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("The 'sparsify' mode of disk '%1$s' can be used 
only for full backup"),
+                           backupdisk->name);
+            return -1;
+        }
     }

     return 0;
diff --git a/src/conf/backup_conf.h b/src/conf/backup_conf.h
index f90a4dcaee..49417ca90d 100644
--- a/src/conf/backup_conf.h
+++ b/src/conf/backup_conf.h
@@ -65,6 +65,8 @@ struct _virDomainBackupDiskDef {
     char *exportname; /* name of the NBD export for pull mode backup */
     char *exportbitmap; /* name of the bitmap exposed in NBD for pull mode 
backup */

+    virTristateBool sparsify; /* sparsify full backup output file */
+
     /* details of target for push-mode, or of the scratch file for pull-mode */
     virStorageSource *store;

diff --git a/src/conf/schemas/domainbackup.rng 
b/src/conf/schemas/domainbackup.rng
index 745a91c33c..7924420815 100644
--- a/src/conf/schemas/domainbackup.rng
+++ b/src/conf/schemas/domainbackup.rng
@@ -175,9 +175,19 @@
               <!-- Allow to plainly select a disk for backup without any other 
config -->
               <group>
                 <ref name="backupAttr"/>
+                <optional>
+                  <attribute name="sparsify">
+                    <ref name="virYesNo"/>
+                  </attribute>
+                </optional>
               </group>
               <group>
                 <ref name="backupAttr"/>
+                <optional>
+                  <attribute name="sparsify">
+                    <ref name="virYesNo"/>
+                  </attribute>
+                </optional>
                 <attribute name="type">
                   <value>file</value>
                 </attribute>
diff --git a/src/qemu/qemu_backup.c b/src/qemu/qemu_backup.c
index b4e5b7347f..d46d488b03 100644
--- a/src/qemu/qemu_backup.c
+++ b/src/qemu/qemu_backup.c
@@ -315,6 +315,11 @@ qemuBackupDiskPrepareDataOne(virDomainObj *vm,
         dd->backingStore = dd->terminator = virStorageSourceNew();
     }

+    if (backupdisk->sparsify == VIR_TRISTATE_BOOL_YES) {
+        backupdisk->store->detect_zeroes = VIR_DOMAIN_DISK_DETECT_ZEROES_UNMAP;
+        backupdisk->store->discard = VIR_DOMAIN_DISK_DISCARD_UNMAP;
+    }
+
     if (qemuDomainPrepareStorageSourceBlockdev(NULL, dd->store, priv, cfg) < 0)
         return -1;

diff --git a/tests/domainbackupxml2xmlin/backup-push-sparsify.xml 
b/tests/domainbackupxml2xmlin/backup-push-sparsify.xml
new file mode 100644
index 0000000000..e8403da037
--- /dev/null
+++ b/tests/domainbackupxml2xmlin/backup-push-sparsify.xml
@@ -0,0 +1,12 @@
+<domainbackup mode="push">
+  <disks>
+    <disk name='vda' type='file' sparsify='no'>
+      <driver type='raw'/>
+      <target file='/path/to/file'/>
+    </disk>
+    <disk name='vdb' type='file' backupmode='full' sparsify='yes'/>
+    <disk name='vdc'/>
+    <disk name='vdd' backup='yes' incremental='test'/>
+    <disk name='hda' backup='no'/>
+  </disks>
+</domainbackup>
diff --git a/tests/domainbackupxml2xmlout/backup-push-sparsify.xml 
b/tests/domainbackupxml2xmlout/backup-push-sparsify.xml
new file mode 100644
index 0000000000..d402f8acd4
--- /dev/null
+++ b/tests/domainbackupxml2xmlout/backup-push-sparsify.xml
@@ -0,0 +1,19 @@
+<domainbackup mode='push'>
+  <disks>
+    <disk name='vda' backup='yes' type='file' backupmode='full' sparsify='no'>
+      <driver type='raw'/>
+      <target file='/path/to/file'/>
+    </disk>
+    <disk name='vdb' backup='yes' type='file' backupmode='full' sparsify='yes'>
+      <target file='/fake/vdb.qcow2.SUFFIX'/>
+    </disk>
+    <disk name='vdc' backup='yes' type='file' backupmode='full'>
+      <target file='/fake/vdc.qcow2.SUFFIX'/>
+    </disk>
+    <disk name='vdd' backup='yes' type='file' backupmode='incremental' 
incremental='test'>
+      <target file='/fake/vdd.qcow2.SUFFIX'/>
+    </disk>
+    <disk name='hda' backup='no'/>
+    <disk name='vdextradisk' backup='no'/>
+  </disks>
+</domainbackup>
diff --git a/tests/genericxml2xmltest.c b/tests/genericxml2xmltest.c
index 269de3501f..7a8074bbe5 100644
--- a/tests/genericxml2xmltest.c
+++ b/tests/genericxml2xmltest.c
@@ -267,6 +267,7 @@ mymain(void)
     DO_TEST_BACKUP("backup-push");
     DO_TEST_BACKUP("backup-push-seclabel");
     DO_TEST_BACKUP("backup-push-encrypted");
+    DO_TEST_BACKUP("backup-push-sparsify");

     DO_TEST_BACKUP_FULL("backup-pull-internal-invalid", true);

-- 
2.56.0

Reply via email to