mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-09-09 09:14:57 +00:00
snapshot: virsh fallback for snapshot-list --descendants --from
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.
This commit is contained in:
parent
16d7b3908e
commit
521cc44700
@ -13177,6 +13177,7 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
|
|||||||
bool tree = vshCommandOptBool(cmd, "tree");
|
bool tree = vshCommandOptBool(cmd, "tree");
|
||||||
const char *from = NULL;
|
const char *from = NULL;
|
||||||
virDomainSnapshotPtr start = NULL;
|
virDomainSnapshotPtr start = NULL;
|
||||||
|
int start_index = -1;
|
||||||
bool descendants = false;
|
bool descendants = false;
|
||||||
|
|
||||||
if (vshCommandOptString(cmd, "from", &from) < 0) {
|
if (vshCommandOptString(cmd, "from", &from) < 0) {
|
||||||
@ -13231,10 +13232,8 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
|
|||||||
numsnaps = ctl->useSnapshotOld ? -1 :
|
numsnaps = ctl->useSnapshotOld ? -1 :
|
||||||
virDomainSnapshotNumChildren(start, flags);
|
virDomainSnapshotNumChildren(start, flags);
|
||||||
if (numsnaps < 0) {
|
if (numsnaps < 0) {
|
||||||
/* XXX also want to emulate --descendants without --tree */
|
if (ctl->useSnapshotOld ||
|
||||||
if ((!descendants || tree) &&
|
last_error->code == VIR_ERR_NO_SUPPORT) {
|
||||||
(ctl->useSnapshotOld ||
|
|
||||||
last_error->code == VIR_ERR_NO_SUPPORT)) {
|
|
||||||
/* We can emulate --from. */
|
/* We can emulate --from. */
|
||||||
virFreeError(last_error);
|
virFreeError(last_error);
|
||||||
last_error = NULL;
|
last_error = NULL;
|
||||||
@ -13312,6 +13311,11 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
|
|||||||
if (tree || ctl->useSnapshotOld) {
|
if (tree || ctl->useSnapshotOld) {
|
||||||
parents = vshCalloc(ctl, sizeof(char *), actual);
|
parents = vshCalloc(ctl, sizeof(char *), actual);
|
||||||
for (i = (from && !ctl->useSnapshotOld); i < actual; i++) {
|
for (i = (from && !ctl->useSnapshotOld); i < actual; i++) {
|
||||||
|
if (ctl->useSnapshotOld && STREQ(names[i], from)) {
|
||||||
|
start_index = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* free up memory from previous iterations of the loop */
|
/* free up memory from previous iterations of the loop */
|
||||||
if (snapshot)
|
if (snapshot)
|
||||||
virDomainSnapshotFree(snapshot);
|
virDomainSnapshotFree(snapshot);
|
||||||
@ -13345,12 +13349,61 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
} else {
|
} else {
|
||||||
if (ctl->useSnapshotOld && descendants) {
|
if (ctl->useSnapshotOld && descendants) {
|
||||||
/* XXX emulate --descendants as well */
|
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"), from);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
for (i = 0; i < actual; i++) {
|
||||||
|
if (i == start_index)
|
||||||
|
continue;
|
||||||
|
if (!parents[i]) {
|
||||||
|
VIR_FREE(names[i]);
|
||||||
|
} else if (STREQ(parents[i], from)) {
|
||||||
|
VIR_FREE(parents[i]);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!changed) {
|
||||||
|
ret = true;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
while (changed) {
|
||||||
|
changed = false;
|
||||||
|
for (i = 0; i < actual; i++) {
|
||||||
|
bool found = false;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
if (!names[i] || !parents[i])
|
||||||
|
continue;
|
||||||
|
for (j = 0; j < actual; j++) {
|
||||||
|
if (!names[j] || i == j)
|
||||||
|
continue;
|
||||||
|
if (STREQ(parents[i], names[j])) {
|
||||||
|
found = true;
|
||||||
|
if (!parents[j])
|
||||||
|
VIR_FREE(parents[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
changed = true;
|
||||||
|
VIR_FREE(names[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VIR_FREE(names[start_index]);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < actual; i++) {
|
for (i = 0; i < actual; i++) {
|
||||||
if (ctl->useSnapshotOld && STRNEQ_NULLABLE(parents[i], from))
|
if (ctl->useSnapshotOld &&
|
||||||
|
(descendants ? !names[i] : STRNEQ_NULLABLE(parents[i], from)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* free up memory from previous iterations of the loop */
|
/* free up memory from previous iterations of the loop */
|
||||||
|
Loading…
Reference in New Issue
Block a user