From: Peter Krempa <pkrempa@redhat.com> 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 <pkrempa@redhat.com> --- 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