backup: Allow configuring incremental backup per-disk individually

The semantics of the backup operation don't strictly require that all
disks being backed up are part of the same incremental part (when a disk
was checkpointed/backed up separately or in a different VM), or even
they may not have a previous checkpoint at all (e.g. when the disk
was freshly hotplugged to the vm).

In such cases we can still create a common checkpoint for all of them
and backup differences according to configuration.

This patch adds a per-disk configuration of the checkpoint to do the
incremental backup from via the 'incremental' attribute and allows
perform full backups via the 'backupmode' attribute.

Note that no changes to the qemu driver are necessary to take advantage
of this as we already obey the per-disk 'incremental' field.

https://bugzilla.redhat.com/show_bug.cgi?id=1829829

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Peter Krempa 2020-06-25 16:16:14 +02:00
parent 6f33e441e2
commit 7e5b993d3b
13 changed files with 133 additions and 17 deletions

View File

@ -69,6 +69,17 @@ were supplied). The following child elements and attributes are supported:
should take part in the backup and using ``no`` excludes the disk from
the backup.
``backupmode``
This attribute overrides the implied backup mode inherited from the
definition of the backup itself. Value ``full`` forces a full backup
even if the backup calls for an incremental backup, and ``incremental``
coupled with the attribute ``incremental='CHECKPOINTNAME`` for the disk
forces an incremental backup from ``CHECKPOINTNAME``.
``incremental``
An optional attribute giving the name of an existing checkpoint of the
domain which overrides the one set by the ``<incremental>`` element.
``exportname``
Allows modification of the NBD export name for the given disk. By
default equal to disk target. Valid only for pull mode backups.

View File

@ -96,6 +96,27 @@
</element>
</define>
<define name='backupDiskMode'>
<optional>
<choice>
<attribute name='backupmode'>
<value>full</value>
</attribute>
<group>
<optional>
<attribute name='backupmode'>
<value>incremental</value>
</attribute>
</optional>
<optional>
<attribute name='incremental'/>
</optional>
</group>
</choice>
</optional>
</define>
<define name='backupPushDriver'>
<optional>
<element name='driver'>
@ -134,6 +155,7 @@
<attribute name='name'>
<ref name='diskTarget'/>
</attribute>
<ref name='backupDiskMode'/>
<choice>
<group>
<attribute name='backup'>
@ -203,6 +225,7 @@
<attribute name='name'>
<ref name='diskTarget'/>
</attribute>
<ref name='backupDiskMode'/>
<optional>
<attribute name='exportname'>
<text/>

View File

@ -56,6 +56,13 @@ VIR_ENUM_IMPL(virDomainBackupDiskState,
"cancelling",
"cancelled");
VIR_ENUM_DECL(virDomainBackupDiskBackupMode);
VIR_ENUM_IMPL(virDomainBackupDiskBackupMode,
VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_LAST,
"",
"full",
"incremental");
void
virDomainBackupDefFree(virDomainBackupDefPtr def)
{
@ -100,6 +107,7 @@ virDomainBackupDiskDefParseXML(xmlNodePtr node,
g_autofree char *driver = NULL;
g_autofree char *backup = NULL;
g_autofree char *state = NULL;
g_autofree char *backupmode = NULL;
int tmp;
xmlNodePtr srcNode;
unsigned int storageSourceParseFlags = 0;
@ -137,6 +145,19 @@ virDomainBackupDiskDefParseXML(xmlNodePtr node,
def->exportbitmap = virXMLPropString(node, "exportbitmap");
}
if ((backupmode = virXMLPropString(node, "backupmode"))) {
if ((tmp = virDomainBackupDiskBackupModeTypeFromString(backupmode)) < 0) {
virReportError(VIR_ERR_XML_ERROR,
_("invalid backupmode '%s' of disk '%s'"),
backupmode, def->name);
return -1;
}
def->backupmode = tmp;
}
def->incremental = virXMLPropString(node, "incremental");
if (internal) {
if (!(state = virXMLPropString(node, "state")) ||
(tmp = virDomainBackupDiskStateTypeFromString(state)) < 0) {
@ -376,6 +397,13 @@ virDomainBackupDiskDefFormat(virBufferPtr buf,
if (disk->backup == VIR_TRISTATE_BOOL_YES) {
virBufferAsprintf(&attrBuf, " type='%s'", virStorageTypeToString(disk->store->type));
if (disk->backupmode != VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_DEFAULT) {
virBufferAsprintf(&attrBuf, " backupmode='%s'",
virDomainBackupDiskBackupModeTypeToString(disk->backupmode));
}
virBufferEscapeString(&attrBuf, " incremental='%s'", disk->incremental);
virBufferEscapeString(&attrBuf, " exportname='%s'", disk->exportname);
virBufferEscapeString(&attrBuf, " exportbitmap='%s'", disk->exportbitmap);
@ -524,6 +552,16 @@ virDomainBackupAlignDisks(virDomainBackupDefPtr def,
return -1;
}
if (backupdisk->backupmode == VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL &&
!backupdisk->incremental &&
!def->incremental) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("'incremental' backup mode of disk '%s' requires setting 'incremental' field for disk or backup"),
backupdisk->name);
return -1;
}
if (backupdisk->backup == VIR_TRISTATE_BOOL_YES &&
virDomainBackupDefAssignStore(backupdisk, domdisk->src, suffix) < 0)
return -1;
@ -561,7 +599,16 @@ virDomainBackupAlignDisks(virDomainBackupDefPtr def,
for (i = 0; i < def->ndisks; i++) {
virDomainBackupDiskDefPtr backupdisk = &def->disks[i];
if (def->incremental && !backupdisk->incremental)
if (backupdisk->backupmode == VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_DEFAULT) {
if (def->incremental || backupdisk->incremental) {
backupdisk->backupmode = VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL;
} else {
backupdisk->backupmode = VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_FULL;
}
}
if (!backupdisk->incremental &&
backupdisk->backupmode == VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL)
backupdisk->incremental = g_strdup(def->incremental);
}

View File

@ -45,12 +45,23 @@ typedef enum {
VIR_DOMAIN_BACKUP_DISK_STATE_LAST
} virDomainBackupDiskState;
typedef enum {
VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_DEFAULT = 0,
VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_FULL,
VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL,
VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_LAST
} virDomainBackupDiskBackupMode;
/* Stores disk-backup information */
typedef struct _virDomainBackupDiskDef virDomainBackupDiskDef;
typedef virDomainBackupDiskDef *virDomainBackupDiskDefPtr;
struct _virDomainBackupDiskDef {
char *name; /* name matching the <target dev='...' of the domain */
virTristateBool backup; /* whether backup is requested */
virDomainBackupDiskBackupMode backupmode;
char *incremental; /* name of the starting point checkpoint of an incremental backup */
char *exportname; /* name of the NBD export for pull mode backup */
char *exportbitmap; /* name of the bitmap exposed in NBD for pull mode backup */

View File

@ -6,5 +6,17 @@
<scratch file='/path/to/file'/>
</disk>
<disk name='hda' backup='no'/>
<disk name='vdc' type='file' backupmode='full'>
<scratch file='/path/to/file'/>
</disk>
<disk name='vdd' type='file' backupmode='incremental'>
<scratch file='/path/to/file'/>
</disk>
<disk name='vde' type='file' backupmode='incremental' incremental='blah'>
<scratch file='/path/to/file'/>
</disk>
<disk name='vdf' type='file' incremental='bleh'>
<scratch file='/path/to/file'/>
</disk>
</disks>
</domainbackup>

View File

@ -2,7 +2,7 @@
<incremental>1525889631</incremental>
<server transport='tcp' tls='yes' name='localhost' port='10809'/>
<disks>
<disk name='vda' backup='yes' type='file' exportname='test-vda' exportbitmap='blah'>
<disk name='vda' backup='yes' type='file' backupmode='incremental' incremental='1525889631' exportname='test-vda' exportbitmap='blah'>
<driver type='qcow2'/>
<scratch file='/path/to/file'>
<encryption format='luks'>
@ -10,7 +10,7 @@
</encryption>
</scratch>
</disk>
<disk name='vdb' backup='yes' type='file' exportname='test-vda' exportbitmap='blah'>
<disk name='vdb' backup='yes' type='file' backupmode='incremental' incremental='1525889631' exportname='test-vda' exportbitmap='blah'>
<driver type='qcow2'/>
<scratch file='/path/to/file'>
<encryption format='luks'>
@ -18,7 +18,7 @@
</encryption>
</scratch>
</disk>
<disk name='vdc' backup='yes' type='block'>
<disk name='vdc' backup='yes' type='block' backupmode='incremental' incremental='1525889631'>
<driver type='qcow2'/>
<scratch dev='/dev/block'>
<encryption format='luks'>

View File

@ -2,7 +2,7 @@
<incremental>1525889631</incremental>
<server transport='tcp' tls='yes' name='localhost' port='10809'/>
<disks>
<disk name='vda' backup='yes' state='running' type='file' exportname='test-vda' exportbitmap='blah'>
<disk name='vda' backup='yes' state='running' type='file' backupmode='incremental' incremental='1525889631' exportname='test-vda' exportbitmap='blah'>
<driver type='qcow2'/>
<scratch file='/path/to/file'>
<encryption format='luks'>
@ -10,7 +10,7 @@
</encryption>
</scratch>
</disk>
<disk name='vdb' backup='yes' state='complete' type='file' exportname='test-vda' exportbitmap='blah'>
<disk name='vdb' backup='yes' state='complete' type='file' backupmode='incremental' incremental='1525889631' exportname='test-vda' exportbitmap='blah'>
<driver type='qcow2'/>
<scratch file='/path/to/file'>
<encryption format='luks'>
@ -18,7 +18,7 @@
</encryption>
</scratch>
</disk>
<disk name='vdc' backup='yes' state='running' type='block'>
<disk name='vdc' backup='yes' state='running' type='block' backupmode='incremental' incremental='1525889631'>
<driver type='qcow2'/>
<scratch dev='/dev/block'>
<encryption format='luks'>

View File

@ -2,13 +2,13 @@
<incremental>1525889631</incremental>
<server transport='tcp' name='localhost' port='10809'/>
<disks>
<disk name='vda' backup='yes' type='file' exportname='test-vda' exportbitmap='blah'>
<disk name='vda' backup='yes' type='file' backupmode='incremental' incremental='1525889631' exportname='test-vda' exportbitmap='blah'>
<driver type='qcow2'/>
<scratch file='/path/to/file'>
<seclabel model='dac' relabel='no'/>
</scratch>
</disk>
<disk name='vdb' backup='yes' type='block'>
<disk name='vdb' backup='yes' type='block' backupmode='incremental' incremental='1525889631'>
<driver type='qcow2'/>
<scratch dev='/dev/block'>
<seclabel model='dac' relabel='no'/>

View File

@ -2,10 +2,22 @@
<incremental>1525889631</incremental>
<server transport='tcp' name='localhost' port='10809'/>
<disks>
<disk name='vda' backup='yes' type='file'>
<disk name='vda' backup='yes' type='file' backupmode='incremental' incremental='1525889631'>
<scratch file='/path/to/file'/>
</disk>
<disk name='hda' backup='no'/>
<disk name='vdc' backup='yes' type='file' backupmode='full'>
<scratch file='/path/to/file'/>
</disk>
<disk name='vdd' backup='yes' type='file' backupmode='incremental' incremental='1525889631'>
<scratch file='/path/to/file'/>
</disk>
<disk name='vde' backup='yes' type='file' backupmode='incremental' incremental='blah'>
<scratch file='/path/to/file'/>
</disk>
<disk name='vdf' backup='yes' type='file' backupmode='incremental' incremental='bleh'>
<scratch file='/path/to/file'/>
</disk>
<disk name='vdextradisk' backup='no'/>
</disks>
</domainbackup>

View File

@ -1,7 +1,7 @@
<domainbackup mode='push'>
<incremental>1525889631</incremental>
<disks>
<disk name='vda' backup='yes' type='file'>
<disk name='vda' backup='yes' type='file' backupmode='incremental' incremental='1525889631'>
<driver type='qcow2'/>
<target file='/path/to/file'>
<encryption format='luks'>
@ -9,7 +9,7 @@
</encryption>
</target>
</disk>
<disk name='vdb' backup='yes' type='file'>
<disk name='vdb' backup='yes' type='file' backupmode='incremental' incremental='1525889631'>
<driver type='raw'/>
<target file='/path/to/file'>
<encryption format='luks'>
@ -17,7 +17,7 @@
</encryption>
</target>
</disk>
<disk name='vdc' backup='yes' type='block'>
<disk name='vdc' backup='yes' type='block' backupmode='incremental' incremental='1525889631'>
<driver type='qcow2'/>
<target dev='/dev/block'>
<encryption format='luks'>

View File

@ -1,13 +1,13 @@
<domainbackup mode='push'>
<incremental>1525889631</incremental>
<disks>
<disk name='vda' backup='yes' type='file'>
<disk name='vda' backup='yes' type='file' backupmode='incremental' incremental='1525889631'>
<driver type='raw'/>
<target file='/path/to/file'>
<seclabel model='dac' relabel='no'/>
</target>
</disk>
<disk name='vdb' backup='yes' type='block'>
<disk name='vdb' backup='yes' type='block' backupmode='incremental' incremental='1525889631'>
<driver type='qcow2'/>
<target dev='/dev/block'>
<seclabel model='dac' relabel='no'/>

View File

@ -1,7 +1,7 @@
<domainbackup mode='push'>
<incremental>1525889631</incremental>
<disks>
<disk name='vda' backup='yes' type='file'>
<disk name='vda' backup='yes' type='file' backupmode='incremental' incremental='1525889631'>
<driver type='raw'/>
<target file='/path/to/file'/>
</disk>

View File

@ -1,6 +1,6 @@
<domainbackup mode='push'>
<disks>
<disk name='vdextradisk' backup='yes' type='file'>
<disk name='vdextradisk' backup='yes' type='file' backupmode='full'>
<target file='/fake/vdextradisk.qcow2.SUFFIX'/>
</disk>
</disks>