On Tue, Mar 26, 2019 at 01:13:52AM -0500, Eric Blake wrote: > Introduce a few more new public APIs related to incremental backups. > This builds on the previous notion of a checkpoint (without an > existing checkpoint, the new API is a full backup, differing from > virDomainBlockCopy in the point of time chosen and in operation on > multiple disks at once); and also allows creation of a new checkpoint > at the same time as starting the backup (after all, an incremental > backup is only useful if it covers the state since the previous > backup). Snapshot creation is also a point in time at which creating > a checkpoint atomically can be useful. A backup job also affects > filtering a listing of domains, as well as adding event reporting for > signaling when a push model backup completes (where the hypervisor > creates the backup); note that the pull model does not have an event > (starting the backup lets a third party access the data, and only the > third party knows when it is finished). > > Since multiple backup jobs can be run in parallel in the future (well, > qemu doesn't support it yet, but we don't want to preclude the idea), > virDomainBackupBegin() returns a job id, which can also be queried by > virDomainListJobIds(), and this job id must be used for > virDomainBackupGetXMLDesc() and virDomainBackupEnd(). In the future, > we may also extend other jobs (migration as the default global job > impacting virDomainJobStats(), and the various block jobs) to all have > ids, where the existing APIs act like thin wrappers around more > powerful APIs that support a job id everywhere. > > The full list of new API: > virDomainBackupBegin; > virDomainBackupEnd; > virDomainBackupGetXMLDesc; > virDomainListJobIds; > virDomainSnapshotCreateXML2; > > Signed-off-by: Eric Blake <eblake@xxxxxxxxxx> > --- > include/libvirt/libvirt-domain-snapshot.h | 7 +- > include/libvirt/libvirt-domain.h | 53 +++- > src/driver-hypervisor.h | 27 ++ > src/qemu/qemu_blockjob.h | 1 + > examples/object-events/event-test.c | 3 + > src/conf/domain_conf.c | 2 +- > src/libvirt-domain-snapshot.c | 89 +++++++ > src/libvirt-domain.c | 285 +++++++++++++++++++++- > src/libvirt_public.syms | 5 + > tools/virsh-domain.c | 8 +- > 10 files changed, 463 insertions(+), 17 deletions(-) > +virDomainSnapshotPtr virDomainSnapshotCreateXML2(virDomainPtr domain, > + const char *xmlDesc, > + const char *snapshotXml, > + unsigned int flags); s/snapshotXml/checkpointXml/ based on later docs. On IRC, you had said an alternative would be to put the checkpointXml as a <domaincheckpoint> child of the main <domainsnapshot> or <domainbackup> XML document. IIUC, the <domaincheckpoint> XML is merely forwarded on the checkpoint APIs. IOW, if you later call virDomainSnapshotGetXMLDesc, you would *not* expect to see the <domaincheckpoint> child again ? If that is correct, then having it via the separate API parameter makes more sense than as a XML child element. I'd only want it as an XML child if that where the canonical representation & storage location. So the separate API looks ok to me. > + > typedef enum { > VIR_DOMAIN_SNAPSHOT_XML_SECURE = VIR_DOMAIN_XML_SECURE, /* dump security sensitive information too */ > } virDomainSnapshotXMLFlags; > diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h > index 94504e70a6..59fb645755 100644 > --- a/include/libvirt/libvirt-domain.h > +++ b/include/libvirt/libvirt-domain.h > @@ -3,7 +3,7 @@ > * Summary: APIs for management of domains > * Description: Provides APIs for the management of domains > * > - * Copyright (C) 2006-2015 Red Hat, Inc. > + * Copyright (C) 2006-2019 Red Hat, Inc. > * > * This library is free software; you can redistribute it and/or > * modify it under the terms of the GNU Lesser General Public > @@ -2420,6 +2420,9 @@ typedef enum { > * exists as long as sync is active */ > VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT = 4, > > + /* Backup (virDomainBackupBegin), job exists until virDomainBackupEnd */ > + VIR_DOMAIN_BLOCK_JOB_TYPE_BACKUP = 5, > + > # ifdef VIR_ENUM_SENTINELS > VIR_DOMAIN_BLOCK_JOB_TYPE_LAST > # endif > @@ -3231,6 +3234,18 @@ int virDomainGetJobStats(virDomainPtr domain, > unsigned int flags); > int virDomainAbortJob(virDomainPtr dom); > > +typedef struct _virDomainJobId virDomainJobId; > +typedef virDomainJobId *virDomainJobIdPtr; > +struct _virDomainJobId { Shouldn't this be called just "virDomainJob" ? Id is just one piece of info inside the struct. Should we be making this struct opaque, and adding virDomainJobGetID and virDomainJobGetType accessors, and thne passing a virDomainJobPtr to the other APIs instead of just an id ? It feels safer if virDomainBackupGetXMLDesc were given the full virDomainJobPtr, as then it can validate that the "type" field represents an backup job. This could detect the case where a stale job ID was passed in that now points to a completely different job type. > + /* One of virDomainJobType */ > + int type; > + > + /* The job id */ > + int id; > +}; > +int virDomainListJobIds(virDomainPtr dom, virDomainJobId **ids, > + unsigned int flags); > + > typedef enum { > VIR_DOMAIN_JOB_OPERATION_UNKNOWN = 0, > VIR_DOMAIN_JOB_OPERATION_START = 1, > @@ -3241,6 +3256,7 @@ typedef enum { > VIR_DOMAIN_JOB_OPERATION_SNAPSHOT = 6, > VIR_DOMAIN_JOB_OPERATION_SNAPSHOT_REVERT = 7, > VIR_DOMAIN_JOB_OPERATION_DUMP = 8, > + VIR_DOMAIN_JOB_OPERATION_BACKUP = 9, > > # ifdef VIR_ENUM_SENTINELS > VIR_DOMAIN_JOB_OPERATION_LAST > @@ -3256,6 +3272,14 @@ typedef enum { > */ > # define VIR_DOMAIN_JOB_OPERATION "operation" > > +/** > + * VIR_DOMAIN_JOB_ID: > + * > + * virDomainGetJobStats field: the id of the job (so far, only for jobs > + * started by virDomainBackupBegin()), as VIR_TYPED_PARAM_INT. > + */ > +# define VIR_DOMAIN_JOB_ID "id" > + > /** > * VIR_DOMAIN_JOB_TIME_ELAPSED: > * > @@ -4080,7 +4104,8 @@ typedef void (*virConnectDomainEventMigrationIterationCallback)(virConnectPtr co > * @nparams: size of the params array > * @opaque: application specific data > * > - * This callback occurs when a job (such as migration) running on the domain > + * This callback occurs when a job (such as migration or push-model > + * virDomainBackupBegin()) running on the domain > * is completed. The params array will contain statistics of the just completed > * job as virDomainGetJobStats would return. The callback must not free @params > * (the array will be freed once the callback finishes). > @@ -4876,4 +4901,28 @@ int virDomainGetLaunchSecurityInfo(virDomainPtr domain, > int *nparams, > unsigned int flags); > > +typedef enum { > + VIR_DOMAIN_BACKUP_BEGIN_NO_METADATA = (1 << 0), /* Make checkpoint without > + remembering it */ > + VIR_DOMAIN_BACKUP_BEGIN_QUIESCE = (1 << 1), /* use guest agent to > + quiesce all mounted > + file systems within > + the domain */ > +} virDomainBackupBeginFlags; > + > +/* Begin an incremental backup job, possibly creating a checkpoint. */ > +int virDomainBackupBegin(virDomainPtr domain, const char *diskXml, > + const char *checkpointXml, unsigned int flags); > + > +/* Learn about an ongoing backup job. */ > +char *virDomainBackupGetXMLDesc(virDomainPtr domain, int id, > + unsigned int flags); > + > +typedef enum { > + VIR_DOMAIN_BACKUP_END_ABORT = (1 << 0), /* Abandon a push model backup */ > +} virDomainBackupEndFlags; > + > +/* Complete (or abort) an incremental backup job. */ > +int virDomainBackupEnd(virDomainPtr domain, int id, unsigned int flags); > + > #endif /* LIBVIRT_DOMAIN_H */ > diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h > index e6efce1bfe..3cdd8a9031 100644 > --- a/src/driver-hypervisor.h > +++ b/src/driver-hypervisor.h > @@ -729,6 +729,11 @@ typedef int > typedef int > (*virDrvDomainAbortJob)(virDomainPtr domain); > > +typedef int > +(*virDrvDomainListJobIds)(virDomainPtr dom, > + virDomainJobId **ids, > + unsigned int flags); > + > typedef int > (*virDrvDomainMigrateGetMaxDowntime)(virDomainPtr domain, > unsigned long long *downtime, > @@ -797,6 +802,12 @@ typedef virDomainSnapshotPtr > const char *xmlDesc, > unsigned int flags); > > +typedef virDomainSnapshotPtr > +(*virDrvDomainSnapshotCreateXML2)(virDomainPtr domain, > + const char *xmlDesc, > + const char *checkpointXml, > + unsigned int flags); > + > typedef char * > (*virDrvDomainSnapshotGetXMLDesc)(virDomainSnapshotPtr snapshot, > unsigned int flags); > @@ -1376,6 +1387,17 @@ typedef int > (*virDrvDomainCheckpointDelete)(virDomainCheckpointPtr checkpoint, > unsigned int flags); > > +typedef int > +(*virDrvDomainBackupBegin)(virDomainPtr domain, const char *diskXml, > + const char *checkpointXml, unsigned int flags); > + > +typedef char * > +(*virDrvDomainBackupGetXMLDesc)(virDomainPtr domain, int id, > + unsigned int flags); > + > +typedef int > +(*virDrvDomainBackupEnd)(virDomainPtr domain, int id, unsigned int flags); > + > typedef struct _virHypervisorDriver virHypervisorDriver; > typedef virHypervisorDriver *virHypervisorDriverPtr; > > @@ -1527,6 +1549,7 @@ struct _virHypervisorDriver { > virDrvDomainGetJobInfo domainGetJobInfo; > virDrvDomainGetJobStats domainGetJobStats; > virDrvDomainAbortJob domainAbortJob; > + virDrvDomainListJobIds domainListJobIds; > virDrvDomainMigrateGetMaxDowntime domainMigrateGetMaxDowntime; > virDrvDomainMigrateSetMaxDowntime domainMigrateSetMaxDowntime; > virDrvDomainMigrateGetCompressionCache domainMigrateGetCompressionCache; > @@ -1541,6 +1564,7 @@ struct _virHypervisorDriver { > virDrvDomainManagedSaveGetXMLDesc domainManagedSaveGetXMLDesc; > virDrvDomainManagedSaveDefineXML domainManagedSaveDefineXML; > virDrvDomainSnapshotCreateXML domainSnapshotCreateXML; > + virDrvDomainSnapshotCreateXML2 domainSnapshotCreateXML2; > virDrvDomainSnapshotGetXMLDesc domainSnapshotGetXMLDesc; > virDrvDomainSnapshotNum domainSnapshotNum; > virDrvDomainSnapshotListNames domainSnapshotListNames; > @@ -1638,6 +1662,9 @@ struct _virHypervisorDriver { > virDrvDomainCheckpointIsCurrent domainCheckpointIsCurrent; > virDrvDomainCheckpointHasMetadata domainCheckpointHasMetadata; > virDrvDomainCheckpointDelete domainCheckpointDelete; > + virDrvDomainBackupBegin domainBackupBegin; > + virDrvDomainBackupGetXMLDesc domainBackupGetXMLDesc; > + virDrvDomainBackupEnd domainBackupEnd; > }; > > > diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h > index c7325c6daf..95f53dde16 100644 > --- a/src/qemu/qemu_blockjob.h > +++ b/src/qemu/qemu_blockjob.h > @@ -55,6 +55,7 @@ typedef enum { > QEMU_BLOCKJOB_TYPE_COPY = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY, > QEMU_BLOCKJOB_TYPE_COMMIT = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT, > QEMU_BLOCKJOB_TYPE_ACTIVE_COMMIT = VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT, > + QEMU_BLOCKJOB_TYPE_BACKUP = VIR_DOMAIN_BLOCK_JOB_TYPE_BACKUP, > /* Additional enum values local to qemu */ > QEMU_BLOCKJOB_TYPE_INTERNAL, > QEMU_BLOCKJOB_TYPE_LAST > diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c > index fcf4492470..98337ad185 100644 > --- a/examples/object-events/event-test.c > +++ b/examples/object-events/event-test.c > @@ -891,6 +891,9 @@ blockJobTypeToStr(int type) > > case VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT: > return "active layer block commit"; > + > + case VIR_DOMAIN_BLOCK_JOB_TYPE_BACKUP: > + return "backup"; > } > > return "unknown"; > diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c > index 5f2b1f68b5..fc45d6c680 100644 > --- a/src/conf/domain_conf.c > +++ b/src/conf/domain_conf.c > @@ -1092,7 +1092,7 @@ VIR_ENUM_IMPL(virDomainOsDefFirmware, > * <mirror> XML (remaining types are not two-phase). */ > VIR_ENUM_DECL(virDomainBlockJob); > VIR_ENUM_IMPL(virDomainBlockJob, VIR_DOMAIN_BLOCK_JOB_TYPE_LAST, > - "", "", "copy", "", "active-commit", > + "", "", "copy", "", "active-commit", "", > ); > > VIR_ENUM_IMPL(virDomainMemoryModel, > diff --git a/src/libvirt-domain-snapshot.c b/src/libvirt-domain-snapshot.c > index 0c8023d9f6..2a50a84417 100644 > --- a/src/libvirt-domain-snapshot.c > +++ b/src/libvirt-domain-snapshot.c > @@ -202,6 +202,10 @@ virDomainSnapshotGetConnect(virDomainSnapshotPtr snapshot) > * block copy operation; in that case, use virDomainBlockJobAbort() > * to stop the block copy first. > * > + * To create a checkpoint object that coincides with the snapshot, in > + * order to facilitate later incremental backups from the time of the > + * snapshot, use virDomainSnapshotCreateXML2(). > + * > * virDomainSnapshotFree should be used to free the resources after the > * snapshot object is no longer needed. > * > @@ -251,6 +255,91 @@ virDomainSnapshotCreateXML(virDomainPtr domain, > } > > > +/** > + * virDomainSnapshotCreateXML2: > + * @domain: a domain object > + * @xmlDesc: string containing an XML description of the domain snapshot > + * @checkpointXml: description of a checkpoint to create or NULL > + * @flags: bitwise-OR of virDomainSnapshotCreateFlags > + * > + * Creates a new snapshot of a domain based on the snapshot xml > + * contained in xmlDesc, with a top-level element <domainsnapshot>. > + * > + * The @checkpointXml parameter is optional; if non-NULL, then libvirt > + * behaves as if virDomainCheckpointCreateXML() were called to create > + * a checkpoint atomically covering the same point in time as the > + * snapshot, using @checkpointXml and forwarding flags > + * VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE and > + * VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA. The creation of a new > + * checkpoint allows for future incremental backups from the time of > + * the snapshot. Note that some hypervisors may require a particular > + * disk format, such as qcow2, in order to take advantage of > + * checkpoints, while allowing arbitrary formats if checkpoints are > + * not involved. This parameter is incompatible with the flag > + * VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE. > + * > + * See virDomainSnapshotCreateXML() for the description of individual > + * flags and general behavior. > + * > + * virDomainSnapshotFree() should be used to free the resources after > + * the snapshot object is no longer needed. > + * > + * Returns an (opaque) new virDomainSnapshotPtr on success or NULL on > + * failure. > + */ > +virDomainSnapshotPtr > +virDomainSnapshotCreateXML2(virDomainPtr domain, > + const char *xmlDesc, > + const char *checkpointXml, > + unsigned int flags) > +{ > + virConnectPtr conn; > + > + VIR_DOMAIN_DEBUG(domain, "xmlDesc=%s, checkpointXml=%s, flags=0x%x", > + xmlDesc, checkpointXml, flags); > + > + virResetLastError(); > + > + virCheckDomainReturn(domain, NULL); > + conn = domain->conn; > + > + virCheckNonNullArgGoto(xmlDesc, error); > + virCheckReadOnlyGoto(conn->flags, error); > + > + VIR_REQUIRE_FLAG_GOTO(VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT, > + VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE, > + error); > + > + VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE, > + VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, > + error); > + VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE, > + VIR_DOMAIN_SNAPSHOT_CREATE_HALT, > + error); > + > + if (checkpointXml && (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE)) { > + virReportInvalidArg(checkpointXml, "%s", > + _("Cannot create checkpoint when redefining " > + "snapshot")); > + goto error; > + } > + > + if (conn->driver->domainSnapshotCreateXML2) { > + virDomainSnapshotPtr ret; > + ret = conn->driver->domainSnapshotCreateXML2(domain, xmlDesc, > + checkpointXml, flags); > + if (!ret) > + goto error; > + return ret; > + } > + > + virReportUnsupportedError(); > + error: > + virDispatchError(conn); > + return NULL; > +} > + > + > /** > * virDomainSnapshotGetXMLDesc: > * @snapshot: a domain snapshot object > diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c > index 1ff4962fc1..45d187099e 100644 > --- a/src/libvirt-domain.c > +++ b/src/libvirt-domain.c > @@ -1,7 +1,7 @@ > /* > * libvirt-domain.c: entry points for virDomainPtr APIs > * > - * Copyright (C) 2006-2015 Red Hat, Inc. > + * Copyright (C) 2006-2019 Red Hat, Inc. > * > * This library is free software; you can redistribute it and/or > * modify it under the terms of the GNU Lesser General Public > @@ -8726,8 +8726,9 @@ virDomainIsUpdated(virDomainPtr dom) > * @domain: a domain object > * @info: pointer to a virDomainJobInfo structure allocated by the user > * > - * Extract information about progress of a background job on a domain. > - * Will return an error if the domain is not active. > + * Extract information about progress of the default background job > + * (id 0) on a domain. Will return an error if the domain is not > + * active. > * > * This function returns a limited amount of information in comparison > * to virDomainGetJobStats(). > @@ -8775,13 +8776,13 @@ virDomainGetJobInfo(virDomainPtr domain, virDomainJobInfoPtr info) > * @nparams: number of items in @params > * @flags: bitwise-OR of virDomainGetJobStatsFlags > * > - * Extract information about progress of a background job on a domain. > - * Will return an error if the domain is not active. The function returns > - * a superset of progress information provided by virDomainGetJobInfo. > - * Possible fields returned in @params are defined by VIR_DOMAIN_JOB_* > - * macros and new fields will likely be introduced in the future so callers > - * may receive fields that they do not understand in case they talk to a > - * newer server. > + * Extract information about progress of the default background job > + * (id 0) on a domain. Will return an error if the domain is not > + * active. The function returns a superset of progress information > + * provided by virDomainGetJobInfo(). Possible fields returned in > + * @params are defined by VIR_DOMAIN_JOB_* macros and new fields will > + * likely be introduced in the future so callers may receive fields > + * that they do not understand in case they talk to a newer server. > * > * When @flags contains VIR_DOMAIN_JOB_STATS_COMPLETED, the function will > * return statistics about a recently completed job. Specifically, this > @@ -8839,7 +8840,7 @@ virDomainGetJobStats(virDomainPtr domain, > * virDomainAbortJob: > * @domain: a domain object > * > - * Requests that the current background job be aborted at the > + * Requests that the current default background job (id 0) be aborted at the > * soonest opportunity. In case the job is a migration in a post-copy mode, > * virDomainAbortJob will report an error (see virDomainMigrateStartPostCopy > * for more details). > @@ -8876,6 +8877,57 @@ virDomainAbortJob(virDomainPtr domain) > } > > > +/** > + * virDomainListJobIds: > + * @domain: a domain object > + * @ids: Pointer to a variable to store the array containing job ids or NULL > + * if the list is not required (just returns number of jobs). > + * @flags: extra flags; not used yet, so callers should always pass 0 > + * > + * Collect a list of all background jobs, and return an allocated > + * array of information about the type and id of each. > + * > + * The default background job (id 0, which is typically migration) > + * might not be included in the list; for that, use This line makes me a little uncomfortable ? Why would we exclude the default background job (sometimes) ? I feel it is preferrable to always return all jobs. > + * virDomainJobStats(). The job id may be important to other APIs > + * related to the job type; for example, a backup job id (created by > + * virDomainBackupBegin()) is required for calling > + * virDomainBackupEnd(). > + * > + * Returns the number of jobs found or -1 and sets @ids to NULL in > + * case of error. The caller is responsible for calling free() on > + * @ids. > + */ > +int > +virDomainListJobIds(virDomainPtr domain, > + virDomainJobId **ids, > + unsigned int flags) > +{ > + virConnectPtr conn; > + > + VIR_DOMAIN_DEBUG(domain); > + > + virResetLastError(); > + > + virCheckDomainReturn(domain, -1); > + conn = domain->conn; > + > + if (conn->driver->domainListJobIds) { > + int ret; > + ret = conn->driver->domainListJobIds(domain, ids, flags); > + if (ret < 0) > + goto error; > + return ret; > + } > + > + virReportUnsupportedError(); > + > + error: > + virDispatchError(conn); > + return -1; > +} > + > + > /** > * virDomainMigrateSetMaxDowntime: > * @domain: a domain object > @@ -10341,6 +10393,12 @@ virDomainBlockRebase(virDomainPtr dom, const char *disk, > * over the destination format, the ability to copy to a destination that > * is not a local file, and the possibility of additional tuning parameters. > * > + * The copy created by this API is not finalized until the job ends, > + * and does not lend itself to incremental backups (beyond what > + * VIR_DOMAIN_BLOCK_COPY_SHALLOW provides) nor to third-party control > + * over the data being copied. For those features, use > + * virDomainBackupBegin(). > + * > * Returns 0 if the operation has started, -1 on failure. > */ > int > @@ -12361,3 +12419,208 @@ int virDomainGetLaunchSecurityInfo(virDomainPtr domain, > virDispatchError(domain->conn); > return -1; > } > + > + > +/** > + * virDomainBackupBegin: > + * @domain: a domain object > + * @diskXml: description of storage to utilize and expose during > + * the backup, or NULL > + * @checkpointXml: description of a checkpoint to create, or NULL > + * @flags: bitwise-OR of supported virDomainBackupBeginFlags > + * > + * Start a point-in-time backup job for the specified disks of a > + * running domain. > + * > + * A backup job is mutually exclusive with domain migration > + * (particularly when the job sets up an NBD export, since it is not > + * possible to tell any NBD clients about a server migrating between > + * hosts). For now, backup jobs are also mutually exclusive with any > + * other block job on the same device, although this restriction may > + * be lifted in a future release. Progress of the backup job can be > + * tracked via virDomainGetJobStats(). The job remains active until a > + * subsequent call to virDomainBackupEnd(), even if it no longer has > + * anything to copy. > + * > + * This API differs from virDomainBlockCopy() because it can grab the > + * state of more than one disk in parallel, and because the state is > + * captured as of the start of the job, rather than the end. > + * > + * There are two fundamental backup approaches. The first, called a > + * push model, instructs the hypervisor to copy the state of the guest > + * disk to the designated storage destination (which may be on the > + * local file system or a network device). In this mode, the > + * hypervisor writes the content of the guest disk to the destination, > + * then emits VIR_DOMAIN_EVENT_ID_JOB_COMPLETED when the backup is > + * either complete or failed (the backup image is invalid if the job > + * fails or virDomainBackupEnd() is used prior to the event being > + * emitted). > + * > + * The second, called a pull model, instructs the hypervisor to expose > + * the state of the guest disk over an NBD export. A third-party > + * client can then connect to this export and read whichever portions > + * of the disk it desires. In this mode, there is no event; libvirt > + * has to be informed via virDomainBackupEnd() when the third-party > + * NBD client is done and the backup resources can be released. > + * > + * The @diskXml parameter is optional but usually provided and > + * contains details about the backup in the top-level element > + * <domainbackup> , including which backup mode to use, whether the > + * backup is incremental from a previous checkpoint, which disks > + * participate in the backup, the destination for a push model backup, > + * and the temporary storage and NBD server details for a pull model > + * backup. If omitted, the backup attempts to default to a push mode > + * full backup of all disks, where libvirt generates a filename for > + * each disk by appending a suffix of a timestamp in seconds since the > + * Epoch. virDomainBackupGetXMLDesc() can be called to learn actual > + * values selected. For more information, see > + * formatcheckpoint.html#BackupAttributes. > + * > + * The @checkpointXml parameter is optional; if non-NULL, then libvirt > + * behaves as if virDomainCheckpointCreateXML() were called to create > + * a checkpoint atomically covering the same point in time as the > + * backup, using @checkpointXml and forwarding flags > + * VIR_DOMAIN_BACKUP_BEGIN_QUIESCE and > + * VIR_DOMAIN_BACKUP_BEGIN_NO_METADATA. The creation of a new > + * checkpoint allows for future incremental backups. Note that some > + * hypervisors may require a particular disk format, such as qcow2, in > + * order to take advantage of checkpoints, while allowing arbitrary > + * formats if checkpoints are not involved. > + * > + * Returns a non-negative job id on success or negative on failure. > + * This id is then passed to virDomainBackupGetXMLDesc() and > + * virDomainBackupEnd(); it can also be obtained from > + * virDomainListJobIds(). This operation returns quickly, such that a > + * user can choose to start a backup job between virDomainFSFreeze() > + * and virDomainFSThaw() in order to create the backup while guest I/O > + * is quiesced. > + */ > +int > +virDomainBackupBegin(virDomainPtr domain, > + const char *diskXml, > + const char *checkpointXml, > + unsigned int flags) > +{ > + virConnectPtr conn; > + > + VIR_DOMAIN_DEBUG(domain, "diskXml=%s, checkpointXml=%s, flags=0x%x", > + NULLSTR(diskXml), NULLSTR(checkpointXml), flags); > + > + virResetLastError(); > + > + virCheckDomainReturn(domain, -1); > + conn = domain->conn; > + > + virCheckReadOnlyGoto(conn->flags, error); > + if (flags & VIR_DOMAIN_BACKUP_BEGIN_NO_METADATA) > + virCheckNonNullArgGoto(checkpointXml, error); > + > + if (conn->driver->domainBackupBegin) { > + int ret; > + ret = conn->driver->domainBackupBegin(domain, diskXml, checkpointXml, > + flags); > + if (!ret) > + goto error; > + return ret; > + } > + > + virReportUnsupportedError(); > + error: > + virDispatchError(conn); > + return -1; > +} > + > + > +/** > + * virDomainBackupGetXMLDesc: > + * @domain: a domain object > + * @id: the id of an active backup job previously started with > + * virDomainBackupBegin() > + * @flags: extra flags; not used yet, so callers should always pass 0 > + * > + * In some cases, a user can start a backup job without supplying all > + * details and rely on libvirt to fill in the rest (for example, > + * selecting the port used for an NBD export). This API can then be > + * used to learn what default values were chosen. At present, none of > + * the information provided is security sensitive. > + * > + * Returns a NUL-terminated UTF-8 encoded XML instance or NULL in > + * case of error. The caller must free() the returned value. > + */ > +char * > +virDomainBackupGetXMLDesc(virDomainPtr domain, int id, unsigned int flags) > +{ > + virConnectPtr conn; > + > + VIR_DOMAIN_DEBUG(domain, "id=%d, flags=0x%x", id, flags); > + > + virResetLastError(); > + > + virCheckDomainReturn(domain, NULL); > + conn = domain->conn; > + > + virCheckNonNegativeArgGoto(id, error); > + > + if (conn->driver->domainBackupGetXMLDesc) { > + char *ret; > + ret = conn->driver->domainBackupGetXMLDesc(domain, id, flags); > + if (!ret) > + goto error; > + return ret; > + } > + > + virReportUnsupportedError(); > + error: > + virDispatchError(conn); > + return NULL; > +} > + > + > +/** > + * virDomainBackupEnd: > + * @domain: a domain object > + * @id: the id of an active backup job previously started with > + * virDomainBackupBegin() > + * @flags: bitwise-OR of supported virDomainBackupEndFlags > + * > + * Conclude a point-in-time backup job @id on the given domain. > + * > + * If the backup job uses the push model, but the event marking that > + * all data has been copied has not yet been emitted, then the command > + * fails unless @flags includes VIR_DOMAIN_BACKUP_END_ABORT. If the > + * event has been issued, or if the backup uses the pull model, the > + * flag has no effect. > + * > + * Returns 1 if the backup job completed successfully (the backup > + * destination file in a push model is consistent), 0 if the job was > + * aborted successfully (only when VIR_DOMAIN_BACKUP_END_ABORT is > + * passed; the destination file is unusable), and -1 on failure. > + */ > +int > +virDomainBackupEnd(virDomainPtr domain, int id, unsigned int flags) > +{ > + virConnectPtr conn; > + > + VIR_DOMAIN_DEBUG(domain, "id=%d, flags=0x%x", id, flags); > + > + virResetLastError(); > + > + virCheckDomainReturn(domain, -1); > + conn = domain->conn; > + > + virCheckReadOnlyGoto(conn->flags, error); > + virCheckNonNegativeArgGoto(id, error); > + > + if (conn->driver->domainBackupEnd) { > + int ret; > + ret = conn->driver->domainBackupEnd(domain, id, flags); > + if (!ret) > + goto error; > + return ret; > + } > + > + virReportUnsupportedError(); > + error: > + virDispatchError(conn); > + return -1; > +} > diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms > index d026b33f53..d13df6bb67 100644 > --- a/src/libvirt_public.syms > +++ b/src/libvirt_public.syms > @@ -817,6 +817,9 @@ LIBVIRT_4.10.0 { > LIBVIRT_5.2.0 { > global: > virConnectGetStoragePoolCapabilities; > + virDomainBackupBegin; > + virDomainBackupEnd; > + virDomainBackupGetXMLDesc; > virDomainCheckpointCreateXML; > virDomainCheckpointCurrent; > virDomainCheckpointDelete; > @@ -833,6 +836,8 @@ LIBVIRT_5.2.0 { > virDomainCheckpointRef; > virDomainHasCurrentCheckpoint; > virDomainListCheckpoints; > + virDomainListJobIds; > + virDomainSnapshotCreateXML2; > } LIBVIRT_4.10.0; > Regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :| -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list