]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
snapshot: Create new virDomainMomentObjList type
authorEric Blake <eblake@redhat.com>
Fri, 22 Mar 2019 05:39:02 +0000 (00:39 -0500)
committerEric Blake <eblake@redhat.com>
Fri, 22 Mar 2019 06:18:34 +0000 (01:18 -0500)
The new code here very heavily resembles the code in
virDomainSnapshotObjList. There are still a couple of spots that are
tied a little too heavily to snapshots (the free function lacks a
polymorphic cleanup until we refactor code to use virObject; and an
upcoming patch will add internal VIR_DOMAIN_MOMENT_LIST flags to
replace the snapshot flag bits), but in general this is fairly close
to the state needed to add checkpoint lists.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Ferlan <jferlan@redhat.com>
src/conf/virconftypes.h
src/conf/virdomainmomentobjlist.c
src/conf/virdomainmomentobjlist.h

index 574815cf049bbc8cb7b02db7b6be1a26b3aded9a..6a8267c4227883f4944d6a22555889907a6d0e33 100644 (file)
@@ -223,6 +223,9 @@ typedef virDomainMomentDef *virDomainMomentDefPtr;
 typedef struct _virDomainMomentObj virDomainMomentObj;
 typedef virDomainMomentObj *virDomainMomentObjPtr;
 
+typedef struct _virDomainMomentObjList virDomainMomentObjList;
+typedef virDomainMomentObjList *virDomainMomentObjListPtr;
+
 typedef struct _virDomainNVRAMDef virDomainNVRAMDef;
 typedef virDomainNVRAMDef *virDomainNVRAMDefPtr;
 
index 209f92493a7f4f9717518fe102fbf71a2444dce2..b2122e7292afbb79abd257a3627d5bd7970c8ac8 100644 (file)
 #include "virstring.h"
 #include "moment_conf.h"
 
+/* FIXME: using virObject would allow us to not need this */
+#include "snapshot_conf.h"
+#include "virdomainsnapshotobjlist.h"
+
 #define VIR_FROM_THIS VIR_FROM_DOMAIN
 
 VIR_LOG_INIT("conf.virdomainmomentobjlist");
 
+/* Opaque struct */
+struct _virDomainMomentObjList {
+    /* name string -> virDomainMomentObj  mapping
+     * for O(1), lockless lookup-by-name */
+    virHashTable *objs;
+
+    virDomainMomentObj metaroot; /* Special parent of all root moments */
+    virDomainMomentObjPtr current; /* The current moment, if any */
+};
+
+
 /* Run iter(data) on all direct children of moment, while ignoring all
  * other entries in moments.  Return the number of children
  * visited.  No particular ordering is guaranteed.  */
@@ -165,3 +180,347 @@ virDomainMomentMoveChildren(virDomainMomentObjPtr from,
     from->nchildren = 0;
     from->first_child = NULL;
 }
+
+
+static virDomainMomentObjPtr
+virDomainMomentObjNew(void)
+{
+    virDomainMomentObjPtr moment;
+
+    if (VIR_ALLOC(moment) < 0)
+        return NULL;
+
+    VIR_DEBUG("obj=%p", moment);
+
+    return moment;
+}
+
+
+static void
+virDomainMomentObjFree(virDomainMomentObjPtr moment)
+{
+    if (!moment)
+        return;
+
+    VIR_DEBUG("obj=%p", moment);
+
+    /* FIXME: Make this polymorphic by inheriting from virObject */
+    virDomainSnapshotDefFree(virDomainSnapshotObjGetDef(moment));
+    VIR_FREE(moment);
+}
+
+
+/* Add def to the list and return a matching object, or NULL on error */
+virDomainMomentObjPtr
+virDomainMomentAssignDef(virDomainMomentObjListPtr moments,
+                         virDomainMomentDefPtr def)
+{
+    virDomainMomentObjPtr moment;
+
+    if (virHashLookup(moments->objs, def->name) != NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unexpected domain moment %s already exists"),
+                       def->name);
+        return NULL;
+    }
+
+    if (!(moment = virDomainMomentObjNew()))
+        return NULL;
+
+    if (virHashAddEntry(moments->objs, moment->def->name, moment) < 0) {
+        VIR_FREE(moment);
+        return NULL;
+    }
+    moment->def = def;
+
+    return moment;
+}
+
+
+static void
+virDomainMomentObjListDataFree(void *payload,
+                               const void *name ATTRIBUTE_UNUSED)
+{
+    virDomainMomentObjPtr obj = payload;
+
+    virDomainMomentObjFree(obj);
+}
+
+
+virDomainMomentObjListPtr
+virDomainMomentObjListNew(void)
+{
+    virDomainMomentObjListPtr moments;
+
+    if (VIR_ALLOC(moments) < 0)
+        return NULL;
+    moments->objs = virHashCreate(50, virDomainMomentObjListDataFree);
+    if (!moments->objs) {
+        VIR_FREE(moments);
+        return NULL;
+    }
+    return moments;
+}
+
+void
+virDomainMomentObjListFree(virDomainMomentObjListPtr moments)
+{
+    if (!moments)
+        return;
+    virHashFree(moments->objs);
+    VIR_FREE(moments);
+}
+
+
+/* Struct and callback for collecting a list of names of moments that
+ * meet a particular filter. */
+struct virDomainMomentNameData {
+    char **const names;
+    int maxnames;
+    unsigned int flags;
+    int count;
+    bool error;
+    virDomainMomentObjListFilter filter;
+};
+
+
+static int virDomainMomentObjListCopyNames(void *payload,
+                                           const void *name ATTRIBUTE_UNUSED,
+                                           void *opaque)
+{
+    virDomainMomentObjPtr obj = payload;
+    struct virDomainMomentNameData *data = opaque;
+
+    if (data->error)
+        return 0;
+    /* Caller already sanitized flags.  Filtering on DESCENDANTS was
+     * done by choice of iteration in the caller.  */
+    /* TODO: Create VIR_DOMAIN_MOMENT_LIST names */
+    if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_LEAVES) && obj->nchildren)
+        return 0;
+    if ((data->flags & VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES) && !obj->nchildren)
+        return 0;
+
+    if (!data->filter(obj, data->flags))
+        return 0;
+
+    if (data->names && data->count < data->maxnames &&
+        VIR_STRDUP(data->names[data->count], obj->def->name) < 0) {
+        data->error = true;
+        return 0;
+    }
+    data->count++;
+    return 0;
+}
+
+
+int
+virDomainMomentObjListGetNames(virDomainMomentObjListPtr moments,
+                               virDomainMomentObjPtr from,
+                               char **const names,
+                               int maxnames,
+                               unsigned int flags,
+                               virDomainMomentObjListFilter filter)
+{
+    struct virDomainMomentNameData data = { names, maxnames, flags, 0,
+                                            false, filter };
+    size_t i;
+
+    if (!from) {
+        /* LIST_ROOTS and LIST_DESCENDANTS have the same bit value,
+         * but opposite semantics.  Toggle here to get the correct
+         * traversal on the metaroot.  */
+        /* TODO: Create VIR_DOMAIN_MOMENT_LIST names */
+        flags ^= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
+        from = &moments->metaroot;
+    }
+
+    /* We handle LIST_ROOT/LIST_DESCENDANTS and LIST_TOPOLOGICAL directly,
+     * mask those bits out to determine when we must use the filter callback. */
+    data.flags &= ~(VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS |
+                    VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL);
+
+    /* If this common code is being used, we assume that all moments
+     * have metadata, and thus can handle METADATA up front as an
+     * all-or-none filter.  XXX This might not always be true, if we
+     * add the ability to track qcow2 internal snapshots without the
+     * use of metadata, in which case this check should move into the
+     * filter callback.  */
+    if ((data.flags & VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA) ==
+        VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA)
+        return 0;
+    data.flags &= ~VIR_DOMAIN_SNAPSHOT_FILTERS_METADATA;
+
+    if (flags & VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS) {
+        /* We could just always do a topological visit; but it is
+         * possible to optimize for less stack usage and time when a
+         * simpler full hashtable visit or counter will do. */
+        if (from->def || (names &&
+                          (flags & VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL)))
+            virDomainMomentForEachDescendant(from,
+                                             virDomainMomentObjListCopyNames,
+                                             &data);
+        else if (names || data.flags)
+            virHashForEach(moments->objs, virDomainMomentObjListCopyNames,
+                           &data);
+        else
+            data.count = virHashSize(moments->objs);
+    } else if (names || data.flags) {
+        virDomainMomentForEachChild(from,
+                                    virDomainMomentObjListCopyNames, &data);
+    } else {
+        data.count = from->nchildren;
+    }
+
+    if (data.error) {
+        for (i = 0; i < data.count; i++)
+            VIR_FREE(names[i]);
+        return -1;
+    }
+
+    return data.count;
+}
+
+
+virDomainMomentObjPtr
+virDomainMomentFindByName(virDomainMomentObjListPtr moments,
+                          const char *name)
+{
+    return name ? virHashLookup(moments->objs, name) : &moments->metaroot;
+}
+
+
+/* Return the current moment, or NULL */
+virDomainMomentObjPtr
+virDomainMomentGetCurrent(virDomainMomentObjListPtr moments)
+{
+    return moments->current;
+}
+
+
+/* Return the current moment's name, or NULL */
+const char *
+virDomainMomentGetCurrentName(virDomainMomentObjListPtr moments)
+{
+    if (moments->current)
+        return moments->current->def->name;
+    return NULL;
+}
+
+
+/* Return true if name matches the current moment */
+bool
+virDomainMomentIsCurrentName(virDomainMomentObjListPtr moments,
+                             const char *name)
+{
+    return moments->current && STREQ(moments->current->def->name, name);
+}
+
+
+/* Update the current moment, using NULL if no current remains */
+void
+virDomainMomentSetCurrent(virDomainMomentObjListPtr moments,
+                          virDomainMomentObjPtr moment)
+{
+    moments->current = moment;
+}
+
+
+/* Return the number of moments in the list */
+int
+virDomainMomentObjListSize(virDomainMomentObjListPtr moments)
+{
+    return virHashSize(moments->objs);
+}
+
+
+/* Remove moment from the list; return true if it was current */
+bool
+virDomainMomentObjListRemove(virDomainMomentObjListPtr moments,
+                             virDomainMomentObjPtr moment)
+{
+    bool ret = moments->current == moment;
+
+    virHashRemoveEntry(moments->objs, moment->def->name);
+    if (ret)
+        moments->current = NULL;
+    return ret;
+}
+
+
+/* Remove all moments tracked in the list */
+void
+virDomainMomentObjListRemoveAll(virDomainMomentObjListPtr moments)
+{
+    virHashRemoveAll(moments->objs);
+    virDomainMomentDropChildren(&moments->metaroot);
+}
+
+
+/* Call iter on each member of the list, in unspecified order */
+int
+virDomainMomentForEach(virDomainMomentObjListPtr moments,
+                       virHashIterator iter,
+                       void *data)
+{
+    return virHashForEach(moments->objs, iter, data);
+}
+
+
+/* Struct and callback function used as a hash table callback; each call
+ * inspects the pre-existing moment->def->parent field, and adjusts
+ * the moment->parent field as well as the parent's child fields to
+ * wire up the hierarchical relations for the given moment.  The error
+ * indicator gets set if a parent is missing or a requested parent would
+ * cause a circular parent chain.  */
+struct moment_set_relation {
+    virDomainMomentObjListPtr moments;
+    int err;
+};
+static int
+virDomainMomentSetRelations(void *payload,
+                            const void *name ATTRIBUTE_UNUSED,
+                            void *data)
+{
+    virDomainMomentObjPtr obj = payload;
+    struct moment_set_relation *curr = data;
+    virDomainMomentObjPtr tmp;
+    virDomainMomentObjPtr parent;
+
+    parent = virDomainMomentFindByName(curr->moments, obj->def->parent);
+    if (!parent) {
+        curr->err = -1;
+        parent = &curr->moments->metaroot;
+        VIR_WARN("moment %s lacks parent", obj->def->name);
+    } else {
+        tmp = parent;
+        while (tmp && tmp->def) {
+            if (tmp == obj) {
+                curr->err = -1;
+                parent = &curr->moments->metaroot;
+                VIR_WARN("moment %s in circular chain", obj->def->name);
+                break;
+            }
+            tmp = tmp->parent;
+        }
+    }
+    virDomainMomentSetParent(obj, parent);
+    return 0;
+}
+
+
+/* Populate parent link and child count of all moments, with all
+ * assigned defs having relations starting as 0/NULL. Return 0 on
+ * success, -1 if a parent is missing or if a circular relationship
+ * was requested. */
+int
+virDomainMomentUpdateRelations(virDomainMomentObjListPtr moments)
+{
+    struct moment_set_relation act = { moments, 0 };
+
+    virDomainMomentDropChildren(&moments->metaroot);
+    virHashForEach(moments->objs, virDomainMomentSetRelations, &act);
+    if (act.err)
+        moments->current = NULL;
+    return act.err;
+}
index dceb55fca273d4c615027e70cdd5cf56a2835483..89a0df5a249bf5dfd35a5d1a8096da826fdf1b36 100644 (file)
 # include "virconftypes.h"
 # include "virhash.h"
 
+/* Filter that returns true if a given moment matches the filter flags */
+typedef bool (*virDomainMomentObjListFilter)(virDomainMomentObjPtr obj,
+                                             unsigned int flags);
+
 /* Struct that allows tracing hierarchical relationships between
  * multiple virDomainMoment objects. The opaque type
  * virDomainMomentObjList then maintains both a hash of these structs
@@ -60,4 +64,33 @@ void virDomainMomentMoveChildren(virDomainMomentObjPtr from,
 void virDomainMomentSetParent(virDomainMomentObjPtr moment,
                               virDomainMomentObjPtr parent);
 
+virDomainMomentObjListPtr virDomainMomentObjListNew(void);
+void virDomainMomentObjListFree(virDomainMomentObjListPtr moments);
+
+virDomainMomentObjPtr virDomainMomentAssignDef(virDomainMomentObjListPtr moments,
+                                               virDomainMomentDefPtr def);
+
+int virDomainMomentObjListGetNames(virDomainMomentObjListPtr moments,
+                                   virDomainMomentObjPtr from,
+                                   char **const names,
+                                   int maxnames,
+                                   unsigned int flags,
+                                   virDomainMomentObjListFilter filter);
+virDomainMomentObjPtr virDomainMomentFindByName(virDomainMomentObjListPtr moments,
+                                                const char *name);
+int virDomainMomentObjListSize(virDomainMomentObjListPtr moments);
+virDomainMomentObjPtr virDomainMomentGetCurrent(virDomainMomentObjListPtr moments);
+const char *virDomainMomentGetCurrentName(virDomainMomentObjListPtr moments);
+bool virDomainMomentIsCurrentName(virDomainMomentObjListPtr moments,
+                                  const char *name);
+void virDomainMomentSetCurrent(virDomainMomentObjListPtr moments,
+                               virDomainMomentObjPtr moment);
+bool virDomainMomentObjListRemove(virDomainMomentObjListPtr moments,
+                                  virDomainMomentObjPtr moment);
+void virDomainMomentObjListRemoveAll(virDomainMomentObjListPtr moments);
+int virDomainMomentForEach(virDomainMomentObjListPtr moments,
+                           virHashIterator iter,
+                           void *data);
+int virDomainMomentUpdateRelations(virDomainMomentObjListPtr moments);
+
 #endif /* LIBVIRT_VIRDOMAINMOMENTOBJLIST_H */