[libvirt] [PATCH v6 8/9] backup: Introduce virDomainBackup APIs

Daniel P. Berrangé berrange at redhat.com
Tue Mar 26 17:08:40 UTC 2019


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 at redhat.com>
> ---
>  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 :|




More information about the libvir-list mailing list