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