Given a list of snapshots and their parents, finding all descendants requires a hairy traversal. This code is O(n^3); it could maybe be made to scale O(n^2) with the use of a hash table, but that costs more memory. Hopefully there aren't too many people with a hierarchy so large as to approach REMOTE_DOMAIN_SNAPSHOT_LIST_NAMES_MAX (1024). * tools/virsh.c (cmdSnapshotList): Add final fallback. --- Applies anywhere after 2.6/4; same story about testing before 3/4. Since I couldn't implement virDomainSnapshotNumChildren for ESX, I figured I'd better go ahead and implement the fallbacks, so that the new snapshot-list features are useful for more than just qemu. tools/virsh.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 48 insertions(+), 6 deletions(-) diff --git a/tools/virsh.c b/tools/virsh.c index 232c9b8..4fbd1a8 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -13113,6 +13113,7 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd) const char *from = NULL; virDomainSnapshotPtr start = NULL; bool emulate_from = false; + int start_index = -1; bool descendants = false; if (vshCommandOptString(cmd, "from", &from) < 0) { @@ -13166,9 +13167,7 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd) } numsnaps = virDomainSnapshotNumChildren(start, flags); if (numsnaps < 0) { - /* XXX also want to emulate --descendants without --tree */ - if ((!descendants || tree) - last_error->code == VIR_ERR_NO_SUPPORT) { + if (last_error->code == VIR_ERR_NO_SUPPORT) { /* We can emulate --from. */ virFreeError(last_error); last_error = NULL; @@ -13244,6 +13243,11 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd) if (tree || emulate_from) { parents = vshCalloc(ctl, sizeof(char *), actual); for (i = (from && !emulate_from); i < actual; i++) { + if (emulate_from && STREQ(names[i], from)) { + start_index = i; + continue; + } + /* free up memory from previous iterations of the loop */ if (snapshot) virDomainSnapshotFree(snapshot); @@ -13277,12 +13281,50 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd) goto cleanup; } else { if (emulate_from && descendants) { - /* XXX emulate --descendants as well */ - goto cleanup; + bool changed = true; + + /* 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. + * Sorry, this is O(n^3) - hope your hierarchy isn't huge. */ + if (start_index < 0) { + vshError(ctl, _("snapshot %s disappeared from list"), from); + goto cleanup; + } + for (i = 0; i < actual; i++) { + if (i == start_index) + continue; + if (!parents[i]) + VIR_FREE(names[i]); + } + while (changed) { + changed = false; + for (i = 0; i < actual; i++) { + bool found = false; + int j; + + if (!names[i] || i == start_index) + continue; + for (j = 0; j < actual; j++) { + if (!names[j] || i == j) + continue; + if (STREQ(parents[i], names[j])) { + found = true; + break; + } + } + if (!found) { + changed = true; + VIR_FREE(names[i]); + } + } + } + VIR_FREE(names[start_index]); } for (i = 0; i < actual; i++) { - if (emulate_from && STRNEQ_NULLABLE(parents[i], from)) + if (emulate_from && + (descendants ? !names[i] : STRNEQ_NULLABLE(parents[i], from))) continue; /* free up memory from previous iterations of the loop */ -- 1.7.4.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list