[libvirt] [PATCH 5/6] snapshot: new virsh function factored from snapshot-list

Eric Blake eblake at redhat.com
Sat Jun 9 04:34:30 UTC 2012


This patch copies just the fallback code out of cmdSnapshotList,
and keeps the snapshot objects around rather than just their
name for easier manipulation.  It looks forward to a future
patch when we add a way to list all snapshot objects at once,
and the next patch will simplify cmdSnapshotList to take
advantage of this factorization.

* tools/virsh.c (vshSnapshotList, vshSnapshotListFree): New functions.
---
 tools/virsh.c |  272 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 272 insertions(+)

diff --git a/tools/virsh.c b/tools/virsh.c
index de7b282..62546b2 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -16568,6 +16568,278 @@ cleanup:
     return ret;
 }

+/* Helpers for collecting a list of snapshots.  */
+struct vshSnap {
+    virDomainSnapshotPtr snap;
+    char *parent;
+};
+struct vshSnapshotList {
+    struct vshSnap *snaps;
+    int nsnaps;
+};
+typedef struct vshSnapshotList *vshSnapshotListPtr;
+
+static void
+vshSnapshotListFree(vshSnapshotListPtr snaplist)
+{
+    int i;
+
+    if (!snaplist)
+        return;
+    if (snaplist->snaps) {
+        for (i = 0; i < snaplist->nsnaps; i++) {
+            if (snaplist->snaps[i].snap)
+                virDomainSnapshotFree(snaplist->snaps[i].snap);
+            VIR_FREE(snaplist->snaps[i].parent);
+        }
+        VIR_FREE(snaplist->snaps);
+    }
+    VIR_FREE(snaplist);
+}
+
+static int
+vshSnapSorter(const void *a, const void *b)
+{
+    const struct vshSnap *sa = a;
+    const struct vshSnap *sb = b;
+
+    if (sa->snap && !sb->snap)
+        return 1;
+    if (!sa->snap && sb->snap)
+        return -1;
+    if (!sa->snap)
+        return 0;
+
+    /* User visible sort, so we want locale-specific case comparison.  */
+    return strcasecmp(virDomainSnapshotGetName(sa->snap),
+                      virDomainSnapshotGetName(sb->snap));
+}
+
+/* Compute a list of snapshots from DOM.  If FROM is provided, the
+ * list is limited to descendants of the given snapshot.  If FLAGS is
+ * given, the list is filtered.  If TREE is specified, then the
+ * parents array of SNAPLIST will also be set in a manner usable by
+ * tree listings (note that parents may also be set when getting
+ * descendants using older servers).  */
+static vshSnapshotListPtr ATTRIBUTE_UNUSED
+vshSnapshotListCollect(vshControl *ctl, virDomainPtr dom,
+                       virDomainSnapshotPtr from,
+                       unsigned int flags, bool tree)
+{
+    int i;
+    char **names = NULL;
+    int count = 0;
+    bool descendants = false;
+    bool roots = false;
+    vshSnapshotListPtr snaplist = vshMalloc(ctl, sizeof(*snaplist));
+    vshSnapshotListPtr ret = NULL;
+    const char *fromname = NULL;
+    int start_index = -1;
+    int deleted = 0;
+
+    /* 0.9.13 will be adding a new listing API.  */
+
+    /* This is the interface available in 0.9.12 and earlier,
+     * including fallbacks to 0.9.4 and earlier.  */
+    if (from) {
+        fromname = virDomainSnapshotGetName(from);
+        if (!fromname) {
+            vshError(ctl, "%s", _("Could not get snapshot name"));
+            goto cleanup;
+        }
+        descendants = (flags & VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS) != 0;
+        if (tree)
+            flags |= VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
+        count = ctl->useSnapshotOld ? -1 :
+            virDomainSnapshotNumChildren(from, flags);
+        if (count < 0) {
+            if (ctl->useSnapshotOld ||
+                last_error->code == VIR_ERR_NO_SUPPORT) {
+                /* We can emulate --from.  */
+                /* XXX can we also emulate --leaves? */
+                virFreeError(last_error);
+                last_error = NULL;
+                ctl->useSnapshotOld = true;
+                flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
+                count = virDomainSnapshotNum(dom, flags);
+            }
+        } else if (tree) {
+            count++;
+        }
+    } else {
+        count = virDomainSnapshotNum(dom, flags);
+
+        /* Fall back to simulation if --roots was unsupported. */
+        /* XXX can we also emulate --leaves? */
+        if (count < 0 && last_error->code == VIR_ERR_INVALID_ARG &&
+            (flags & VIR_DOMAIN_SNAPSHOT_LIST_ROOTS)) {
+            virFreeError(last_error);
+            last_error = NULL;
+            roots = true;
+            flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
+            count = virDomainSnapshotNum(dom, flags);
+        }
+    }
+
+    if (count < 0) {
+        if (!last_error)
+            vshError(ctl, _("missing support"));
+        goto cleanup;
+    }
+
+    if (!count)
+        goto success;
+
+    names = vshCalloc(ctl, sizeof(*names), count);
+
+    if (from && !ctl->useSnapshotOld) {
+        if (tree) {
+            if (count)
+                count = virDomainSnapshotListChildrenNames(from, names + 1,
+                                                           count - 1, flags);
+            if (count >= 0) {
+                count++;
+                names[0] = vshStrdup(ctl, fromname);
+            }
+        } else {
+            count = virDomainSnapshotListChildrenNames(from, names,
+                                                       count, flags);
+        }
+    } else {
+        count = virDomainSnapshotListNames(dom, names, count, flags);
+    }
+    if (count < 0)
+        goto cleanup;
+
+    snaplist->snaps = vshCalloc(ctl, sizeof(*snaplist->snaps), count);
+    snaplist->nsnaps = count;
+    for (i = 0; i < count; i++) {
+        snaplist->snaps[i].snap = virDomainSnapshotLookupByName(dom,
+                                                                names[i], 0);
+        if (!snaplist->snaps[i].snap)
+            goto cleanup;
+    }
+
+    if (tree || (from && ctl->useSnapshotOld)) {
+        for (i = (from && !ctl->useSnapshotOld); i < count; i++) {
+            if (from && ctl->useSnapshotOld && STREQ(names[i], fromname)) {
+                start_index = i;
+                continue;
+            }
+            if (vshGetSnapshotParent(ctl, snaplist->snaps[i].snap,
+                                     &snaplist->snaps[i].parent) < 0)
+                goto cleanup;
+        }
+    }
+    if (tree) {
+        if (from && ctl->useSnapshotOld) {
+            /* Leave things so that the only entry without a parent is
+             * 'from'.  */
+            for (i = 0; i < count; i++) {
+                if (STREQ(virDomainSnapshotGetName(snaplist->snaps[i].snap),
+                          fromname)) {
+                    VIR_FREE(snaplist->snaps[i].parent);
+                } else if (!snaplist->snaps[i].parent) {
+                    virDomainSnapshotFree(snaplist->snaps[i].snap);
+                    snaplist->snaps[i].snap = NULL;
+                    deleted++;
+                }
+            }
+        }
+        goto success;
+    }
+
+    if (ctl->useSnapshotOld && descendants) {
+        bool changed = false;
+
+        /* Make multiple passes over the list - first pass NULLs out
+         * all roots except start, remaining passes NULL out any entry
+         * whose parent is not still in list.  Also, we NULL out
+         * parent when name is known to be in list.  Sorry, this is
+         * O(n^3) - hope your hierarchy isn't huge.  */
+        if (start_index < 0) {
+            vshError(ctl, _("snapshot %s disappeared from list"), fromname);
+            goto cleanup;
+        }
+        for (i = 0; i < count; i++) {
+            if (i == start_index)
+                continue;
+            if (!snaplist->snaps[i].parent) {
+                VIR_FREE(names[i]);
+                virDomainSnapshotFree(snaplist->snaps[i].snap);
+                snaplist->snaps[i].snap = NULL;
+                VIR_FREE(snaplist->snaps[i].parent);
+                deleted++;
+            } else if (STREQ(snaplist->snaps[i].parent, fromname)) {
+                VIR_FREE(snaplist->snaps[i].parent);
+                changed = true;
+            }
+        }
+        if (!changed)
+            goto success;
+        while (changed) {
+            changed = false;
+            for (i = 0; i < count; i++) {
+                bool found = false;
+                int j;
+
+                if (!names[i] || !snaplist->snaps[i].parent)
+                    continue;
+                for (j = 0; j < count; j++) {
+                    if (!names[j] || i == j)
+                        continue;
+                    if (STREQ(snaplist->snaps[i].parent, names[j])) {
+                        found = true;
+                        if (!snaplist->snaps[j].parent)
+                            VIR_FREE(snaplist->snaps[i].parent);
+                        break;
+                    }
+                }
+                if (!found) {
+                    changed = true;
+                    VIR_FREE(names[i]);
+                    virDomainSnapshotFree(snaplist->snaps[i].snap);
+                    snaplist->snaps[i].snap = NULL;
+                    VIR_FREE(snaplist->snaps[i].parent);
+                    deleted++;
+                }
+            }
+        }
+        VIR_FREE(names[start_index]);
+        virDomainSnapshotFree(snaplist->snaps[start_index].snap);
+        snaplist->snaps[start_index].snap = NULL;
+        VIR_FREE(snaplist->snaps[start_index].parent);
+        deleted++;
+    }
+    if (roots) {
+        for (i = 0; i < count; i++) {
+            if (snaplist->snaps[i].parent) {
+                VIR_FREE(names[i]);
+                virDomainSnapshotFree(snaplist->snaps[i].snap);
+                snaplist->snaps[i].snap = NULL;
+                VIR_FREE(snaplist->snaps[i].parent);
+                deleted++;
+            }
+        }
+    }
+
+success:
+    qsort(snaplist->snaps, count, sizeof(*snaplist->snaps), vshSnapSorter);
+
+    snaplist->nsnaps -= deleted;
+
+    ret = snaplist;
+    snaplist = NULL;
+
+cleanup:
+    vshSnapshotListFree(snaplist);
+    if (names)
+        for (i = 0; i < count; i++)
+            VIR_FREE(names[i]);
+    VIR_FREE(names);
+    return ret;
+}
+
 /*
  * "snapshot-list" command
  */
-- 
1.7.10.2




More information about the libvir-list mailing list