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