[libvirt] [PATCH v4 08/20] backup: Introduce virDomainBackup APIs

John Ferlan jferlan at redhat.com
Mon Feb 11 21:21:13 UTC 2019



On 2/6/19 2:18 PM, 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 only
> from virDomainCopy in the point of time chosen); 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).  It also enhances 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).
> 
> The full list of new API:
>         virDomainBackupBegin;
>         virDomainBackupEnd;
>         virDomainBackupGetXMLDesc;
> 
> Signed-off-by: Eric Blake <eblake at redhat.com>
> ---
>  include/libvirt/libvirt-domain-checkpoint.h |  21 ++
>  include/libvirt/libvirt-domain.h            |  17 +-
>  src/driver-hypervisor.h                     |  14 ++
>  src/qemu/qemu_blockjob.h                    |   1 +
>  examples/object-events/event-test.c         |   3 +
>  src/conf/domain_conf.c                      |   2 +-
>  src/libvirt-domain-checkpoint.c             | 202 ++++++++++++++++++++
>  src/libvirt-domain.c                        |   8 +-
>  src/libvirt_public.syms                     |   3 +
>  tools/virsh-domain.c                        |   8 +-
>  10 files changed, 273 insertions(+), 6 deletions(-)
> 
> diff --git a/include/libvirt/libvirt-domain-checkpoint.h b/include/libvirt/libvirt-domain-checkpoint.h
> index 9006a46c6e..6c3a85a1bf 100644
> --- a/include/libvirt/libvirt-domain-checkpoint.h
> +++ b/include/libvirt/libvirt-domain-checkpoint.h
> @@ -152,4 +152,25 @@ int virDomainCheckpointDelete(virDomainCheckpointPtr checkpoint,
>  int virDomainCheckpointRef(virDomainCheckpointPtr checkpoint);
>  int virDomainCheckpointFree(virDomainCheckpointPtr checkpoint);
> 
> +typedef enum {
> +    VIR_DOMAIN_BACKUP_BEGIN_NO_METADATA = (1 << 0), /* Make checkpoint without
> +                                                       remembering it */
> +    /* TODO: VIR_DOMAIN_BACKUP_BEGIN_QUIESCE */

Implement or drop TODO:

> +} 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_CHECKPOINT_H */
> diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h
> index 0388911ded..cc23029c97 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
> @@ -2394,6 +2394,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
> @@ -3215,6 +3218,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
> @@ -3230,6 +3234,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:
>   *
> @@ -4054,7 +4066,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).
> diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h
> index 14db8e49f3..4e65b8ad36 100644
> --- a/src/driver-hypervisor.h
> +++ b/src/driver-hypervisor.h
> @@ -1376,6 +1376,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;
> 
> @@ -1638,6 +1649,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 0221eb0634..fa3db9266f 100644
> --- a/src/conf/domain_conf.c
> +++ b/src/conf/domain_conf.c
> @@ -1030,7 +1030,7 @@ VIR_ENUM_IMPL(virDomainHPTResizing,
>   * <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-checkpoint.c b/src/libvirt-domain-checkpoint.c
> index 8a7b5b3c56..29cefe41f1 100644
> --- a/src/libvirt-domain-checkpoint.c
> +++ b/src/libvirt-domain-checkpoint.c
> @@ -721,3 +721,205 @@ virDomainCheckpointFree(virDomainCheckpointPtr checkpoint)
>      virObjectUnref(checkpoint);
>      return 0;
>  }

Dirtying the namespace of libvirt-domain-checkpoint w/ Backup. Why not a
separate libvirt-domain-backup - beyond the obvious need to alter more
stuff of course.  If kept here would seem to need to alter line #2 way
back up at the top to include virDomainBackupPtr API's too.

> +
> +
> +/**
> + * 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() in that it can grab the
> + * state of more than one disk in parallel, and the state is captured

s/, and/ and/

> + * 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

s/; in/. In/

> + * 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
> + * is ended prior to the event being emitted).  The second, called a

s/is ended/ends/  (or fails ?)

> + * pull model, instructs the hypervisor to expose the state of the
> + * guest disk over an NBD export; a third-party client can then

s/; a/. A/

> + * connect to this export, and read whichever portions of the disk it

s/, and/ and/

> + * desires.  In this mode, there is no event; libvirt has to be
> + * informed when the third-party NBD client is done and the backup
> + * resources can be released.
> + *
> + * The @diskXml parameter is optional but usually provided, and

s/, and/ and/

> + * contains details about the backup, 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 with
> + * @checkpointXml and the flag VIR_DOMAIN_BACKUP_BEGIN_NO_METADATA
> + * forwarded appropriately, atomically covering the same guest state

s/, / and /

> + * that will be part of the backup.  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.

s/, or/ or/

> + * 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.
> + */
> +/* FIXME: Do we need a specific API for listing all current backup
> + * jobs (which, at the moment, is at most one job), or is it better to
> + * refactor other existing job APIs in libvirt-domain.c to have job-id
> + * counterparts along with a generic listing of all jobs (with flags
> + * for filtering to specific job types)?
> + */

Or do we wait until some consumer asks for this? Do you mean
GetBlockJobInfo? GetJobStats? GetJobInfo?

Is it required for initial implementation?  Not sure I'm expert enough
to answer that questions!

> +int
> +virDomainBackupBegin(virDomainPtr domain, const char *diskXml,
> +                     const char *checkpointXml, unsigned int flags)

One line per argument

> +{
> +    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,

s/, and/ and/

> + * selecting the port used for an NBD export). This API can then be
> + * used to learn what default values were chosen.
> + *
> + * Returns a NUL-terminated UTF-8 encoded XML instance, or NULL in

s/, or/ or/

> + * case of error.  The caller must free() the returned value.
> + */

Do we have the same security concerns as Checkpoint?  IOW: One doesn't
necessarily have to use a conn that was used for virDomainBackupBegin or
do they?  Should we just check for a non read-only connection (which I
assume has implications later in remote.x defs).

> +char *
> +virDomainBackupGetXMLDesc(virDomainPtr domain, int id, unsigned int flags)

One line per argument

> +{
> +    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.
> + */

So 0 can only be returned if/when ABORT is used (just checking my
reading...)

> +int
> +virDomainBackupEnd(virDomainPtr domain, int id, unsigned int flags)

One line per argument


John

> +{
> +    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-domain.c b/src/libvirt-domain.c
> index 58b4863c8f..a159b9d78c 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-2018 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
> @@ -10325,6 +10325,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
> diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
> index 1b18402e5e..96858388d1 100644
> --- a/src/libvirt_public.syms
> +++ b/src/libvirt_public.syms
> @@ -816,6 +816,9 @@ LIBVIRT_4.10.0 {
> 
>  LIBVIRT_5.1.0 {
>      global:
> +        virDomainBackupBegin;
> +        virDomainBackupEnd;
> +        virDomainBackupGetXMLDesc;
>          virDomainCheckpointCreateXML;
>          virDomainCheckpointCurrent;
>          virDomainCheckpointDelete;
> diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
> index 275ac0c318..68d7dc6df7 100644
> --- a/tools/virsh-domain.c
> +++ b/tools/virsh-domain.c
> @@ -2561,7 +2561,9 @@ VIR_ENUM_IMPL(virshDomainBlockJob,
>                N_("Block Pull"),
>                N_("Block Copy"),
>                N_("Block Commit"),
> -              N_("Active Block Commit"));
> +              N_("Active Block Commit"),
> +              N_("Backup"),
> +);
> 
>  static const char *
>  virshDomainBlockJobToString(int type)
> @@ -6064,7 +6066,9 @@ VIR_ENUM_IMPL(virshDomainJobOperation,
>                N_("Outgoing migration"),
>                N_("Snapshot"),
>                N_("Snapshot revert"),
> -              N_("Dump"));
> +              N_("Dump"),
> +              N_("Backup"),
> +);
> 
>  static const char *
>  virshDomainJobOperationToString(int op)
> 




More information about the libvir-list mailing list