[libvirt] [PATCH v8 07/21] backup: Allow for lists of checkpoint objects

Peter Krempa pkrempa at redhat.com
Thu Apr 25 12:45:10 UTC 2019


On Wed, Apr 17, 2019 at 09:09:07 -0500, Eric Blake wrote:
> Create a new file for managing a list of checkpoint objects, borrowing
> heavily from existing virDomainSnapshotObjList paradigms.
> 
> Note that while checkpoints definitely have a use case for multiple
> children to a single parent (create a base snapshot, create a child
> snapshot, revert to the base, then create another child snapshot),
> it's harder to predict how checkpoints will play out with reverting to
> prior points in time. Thus, in initial use, we may find that in a list
> of checkpoints, you never have more than one child.  However, as the
> snapshot machinery is already generic, it is easier to reuse the
> generic machinery that tracks relations between domain moments than it
> is to open-code a new list-management scheme just for checkpoints.
> 
> The virDomainMomentObjList code is not quite polymorphic enough until
> we patch virDomainMomentDef to be a proper virObject, but doing that
> is a bigger audit.  So for now, I had to duplicate the cleanup calls
> based on a bool flag for which type needs cleaning. Oh well.

I'm afraid that with this being committed the motivation to refactor it
properly will be gone.

> 
> Signed-off-by: Eric Blake <eblake at redhat.com>
> ---
>  src/conf/checkpoint_conf.h            |   7 +
>  src/conf/domain_conf.h                |   2 +
>  src/conf/virconftypes.h               |   6 +
>  src/conf/virdomaincheckpointobjlist.h |  74 +++++++++
>  src/conf/virdomainmomentobjlist.h     |   2 +-
>  src/conf/virdomainobjlist.h           |   7 +-
>  src/conf/Makefile.inc.am              |   2 +
>  src/conf/checkpoint_conf.c            |  86 ++++++++++
>  src/conf/domain_conf.c                |   6 +
>  src/conf/virdomaincheckpointobjlist.c | 223 ++++++++++++++++++++++++++
>  src/conf/virdomainmomentobjlist.c     |  40 ++++-
>  src/conf/virdomainobjlist.c           |  11 ++
>  src/conf/virdomainsnapshotobjlist.c   |   2 +-
>  src/libvirt_private.syms              |  18 +++
>  14 files changed, 477 insertions(+), 9 deletions(-)
>  create mode 100644 src/conf/virdomaincheckpointobjlist.h
>  create mode 100644 src/conf/virdomaincheckpointobjlist.c

[...]

> diff --git a/src/conf/checkpoint_conf.c b/src/conf/checkpoint_conf.c
> index 87f76e6518..cb0d71936f 100644
> --- a/src/conf/checkpoint_conf.c
> +++ b/src/conf/checkpoint_conf.c
> @@ -35,6 +35,7 @@
>  #include "virerror.h"
>  #include "virxml.h"
>  #include "virstring.h"
> +#include "virdomaincheckpointobjlist.h"
> 
>  #define VIR_FROM_THIS VIR_FROM_DOMAIN_CHECKPOINT
> 
> @@ -548,3 +549,88 @@ virDomainCheckpointDefFormat(virDomainCheckpointDefPtr def,
> 
>      return virBufferContentAndReset(&buf);
>  }
> +
> +
> +int
> +virDomainCheckpointRedefinePrep(virDomainPtr domain,
> +                                virDomainObjPtr vm,
> +                                virDomainCheckpointDefPtr *defptr,
> +                                virDomainMomentObjPtr *chk,
> +                                virDomainXMLOptionPtr xmlopt,
> +                                bool *update_current)
> +{
> +    virDomainCheckpointDefPtr def = *defptr;
> +    char uuidstr[VIR_UUID_STRING_BUFLEN];
> +    virDomainMomentObjPtr other;
> +    virDomainCheckpointDefPtr otherdef;
> +
> +    virUUIDFormat(domain->uuid, uuidstr);
> +
> +    /* TODO: Move this and snapshot version into virDomainMoment */
> +    /* Prevent circular chains */
> +    if (def->common.parent) {
> +        if (STREQ(def->common.name, def->common.parent)) {
> +            virReportError(VIR_ERR_INVALID_ARG,
> +                           _("cannot set checkpoint %s as its own parent"),
> +                           def->common.name);
> +            return -1;
> +        }
> +        other = virDomainCheckpointFindByName(vm->checkpoints, def->common.parent);
> +        if (!other) {
> +            virReportError(VIR_ERR_INVALID_ARG,
> +                           _("parent %s for checkpoint %s not found"),
> +                           def->common.parent, def->common.name);
> +            return -1;
> +        }
> +        otherdef = virDomainCheckpointObjGetDef(other);
> +        while (otherdef->common.parent) {
> +            if (STREQ(otherdef->common.parent, def->common.name)) {
> +                virReportError(VIR_ERR_INVALID_ARG,
> +                               _("parent %s would create cycle to %s"),
> +                               otherdef->common.name, def->common.name);
> +                return -1;
> +            }
> +            other = virDomainCheckpointFindByName(vm->checkpoints,
> +                                                  otherdef->common.parent);
> +            if (!other) {
> +                VIR_WARN("checkpoints are inconsistent for %s",
> +                         vm->def->name);
> +                break;
> +            }
> +            otherdef = virDomainCheckpointObjGetDef(other);
> +        }
> +    }
> +
> +    if (!def->common.dom ||
> +        memcmp(def->common.dom->uuid, domain->uuid, VIR_UUID_BUFLEN)) {
> +        virReportError(VIR_ERR_INVALID_ARG,
> +                       _("definition for checkpoint %s must use uuid %s"),
> +                       def->common.name, uuidstr);
> +        return -1;
> +    }
> +    if (virDomainCheckpointAlignDisks(def) < 0)
> +        return -1;
> +
> +    other = virDomainCheckpointFindByName(vm->checkpoints, def->common.name);
> +    otherdef = other ? virDomainCheckpointObjGetDef(other) : NULL;
> +    if (other) {
> +        if (!virDomainDefCheckABIStability(otherdef->common.dom,
> +                                           def->common.dom, xmlopt))
> +            return -1;
> +
> +        if (other == virDomainCheckpointGetCurrent(vm->checkpoints)) {
> +            *update_current = true;
> +            virDomainCheckpointSetCurrent(vm->checkpoints, NULL);
> +        }
> +
> +        /* Drop and rebuild the parent relationship, but keep all
> +         * child relations by reusing chk.  */
> +        virDomainMomentDropParent(other);
> +        virDomainCheckpointDefFree(otherdef);
> +        other->def = &(*defptr)->common;
> +        *defptr = NULL;
> +        *chk = other;
> +    }
> +
> +    return 0;
> +}

[...]

> diff --git a/src/conf/virdomaincheckpointobjlist.c b/src/conf/virdomaincheckpointobjlist.c
> new file mode 100644
> index 0000000000..dbc1c25c5e
> --- /dev/null
> +++ b/src/conf/virdomaincheckpointobjlist.c
> @@ -0,0 +1,223 @@

[...]

> +
> +#define VIR_FROM_THIS VIR_FROM_DOMAIN_CHECKPOINT
> +
> +VIR_LOG_INIT("conf.virdomaincheckpointobjlist");
> +
> +struct _virDomainCheckpointObjList {
> +    virDomainMomentObjListPtr base;
> +};

Is this just for compile time typechecking?

> +virDomainMomentObjPtr
> +virDomainCheckpointAssignDef(virDomainCheckpointObjListPtr checkpoints,
> +                             virDomainCheckpointDefPtr def)
> +{
> +    return virDomainMomentAssignDef(checkpoints->base, &def->common);
> +}
> +
> +
> +static bool
> +virDomainCheckpointFilter(virDomainMomentObjPtr obj ATTRIBUTE_UNUSED,
> +                          unsigned int flags)
> +{
> +    /* For now, we have no further filters than what the common code handles. */
> +    virCheckFlags(0, false);
> +    return true;
> +}
> +
> +
> +virDomainCheckpointObjListPtr
> +virDomainCheckpointObjListNew(void)
> +{
> +    virDomainCheckpointObjListPtr checkpoints;
> +
> +    if (VIR_ALLOC(checkpoints) < 0)
> +        return NULL;
> +    checkpoints->base = virDomainMomentObjListNew(false);
> +    if (!checkpoints->base) {
> +        VIR_FREE(checkpoints);
> +        return NULL;
> +    }
> +    return checkpoints;
> +}
> +
> +
> +void
> +virDomainCheckpointObjListFree(virDomainCheckpointObjListPtr checkpoints)
> +{
> +    if (!checkpoints)
> +        return;
> +    virDomainMomentObjListFree(checkpoints->base);
> +    VIR_FREE(checkpoints);
> +}
> +
> +
> +static int
> +virDomainCheckpointObjListGetNames(virDomainCheckpointObjListPtr checkpoints,
> +                                   virDomainMomentObjPtr from,
> +                                   char **const names,
> +                                   int maxnames,
> +                                   unsigned int flags)
> +{
> +    /* We intentionally chose our public flags to match the common flags */
> +    verify(VIR_DOMAIN_CHECKPOINT_LIST_ROOTS ==
> +           (int) VIR_DOMAIN_MOMENT_LIST_ROOTS);
> +    verify(VIR_DOMAIN_CHECKPOINT_LIST_TOPOLOGICAL ==
> +           (int) VIR_DOMAIN_MOMENT_LIST_TOPOLOGICAL);
> +    verify(VIR_DOMAIN_CHECKPOINT_LIST_LEAVES ==
> +           (int) VIR_DOMAIN_MOMENT_LIST_LEAVES);
> +    verify(VIR_DOMAIN_CHECKPOINT_LIST_NO_LEAVES ==
> +           (int) VIR_DOMAIN_MOMENT_LIST_NO_LEAVES);
> +    verify(VIR_DOMAIN_CHECKPOINT_LIST_METADATA ==
> +           (int) VIR_DOMAIN_MOMENT_LIST_METADATA);
> +    verify(VIR_DOMAIN_CHECKPOINT_LIST_NO_METADATA ==
> +           (int) VIR_DOMAIN_MOMENT_LIST_NO_METADATA);

This looks like it should be near the VIR_DOMAIN_CHECKPOINT_LIST enum
rather than somewhere in the code randomly.

> +
> +    return virDomainMomentObjListGetNames(checkpoints->base, from, names,
> +                                          maxnames, flags,
> +                                          virDomainCheckpointFilter, 0);
> +}

[...]

> diff --git a/src/conf/virdomainmomentobjlist.c b/src/conf/virdomainmomentobjlist.c
> index 7f90632a53..3dd6da7acb 100644
> --- a/src/conf/virdomainmomentobjlist.c
> +++ b/src/conf/virdomainmomentobjlist.c

[...]

> @@ -242,23 +258,35 @@ virDomainMomentAssignDef(virDomainMomentObjListPtr moments,
> 
> 
>  static void
> -virDomainMomentObjListDataFree(void *payload,
> -                               const void *name ATTRIBUTE_UNUSED)
> +virDomainCheckpointObjListDataFree(void *payload,
> +                                   const void *name ATTRIBUTE_UNUSED)
>  {
>      virDomainMomentObjPtr obj = payload;
> 
> -    virDomainMomentObjFree(obj);
> +    virDomainCheckpointObjFree(obj);
> +}
> +
> +
> +static void
> +virDomainSnapshotObjListDataFree(void *payload,
> +                                 const void *name ATTRIBUTE_UNUSED)
> +{
> +    virDomainMomentObjPtr obj = payload;
> +
> +    virDomainSnapshotObjFree(obj);
>  }
> 
> 
>  virDomainMomentObjListPtr
> -virDomainMomentObjListNew(void)
> +virDomainMomentObjListNew(bool snapshot)

Please create a new entrypoint for this. I don't see the need to
overload it using the argument.

>  {
>      virDomainMomentObjListPtr moments;
> 
>      if (VIR_ALLOC(moments) < 0)
>          return NULL;
> -    moments->objs = virHashCreate(50, virDomainMomentObjListDataFree);
> +    moments->objs = virHashCreate(50,
> +                                  snapshot ? virDomainSnapshotObjListDataFree :
> +                                  virDomainCheckpointObjListDataFree);
>      if (!moments->objs) {
>          VIR_FREE(moments);
>          return NULL;
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20190425/87aadcd7/attachment-0001.sig>


More information about the libvir-list mailing list