Sometimes, we only care about one branch of the snapshot hierarchy.
Make it easier to list a single branch, by using the new APIs.
Technically, I could emulate these new virsh options on old servers
by doing a complete dump, then scraping xml to filter out just the
snapshots that I care about, but I didn't want to do that in this patch.
* tools/virsh.c (cmdSnapshotList): Add --from, --descendants.
* tools/virsh.pod (snapshot-list): Document them.
---
tools/virsh.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++-------
tools/virsh.pod | 9 +++++++-
2 files changed, 63 insertions(+), 10 deletions(-)
diff --git a/tools/virsh.c b/tools/virsh.c
index 1909dce..f43af7e 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -13081,6 +13081,8 @@ static const vshCmdOptDef opts_snapshot_list[] = {
{"metadata", VSH_OT_BOOL, 0,
N_("list only snapshots that have metadata that would prevent
undefine")},
{"tree", VSH_OT_BOOL, 0, N_("list snapshots in a tree")},
+ {"from", VSH_OT_DATA, 0, N_("limit list to children of given
snapshot")},
+ {"descendants", VSH_OT_BOOL, 0, N_("with --from, list all
descendants")},
{NULL, 0, 0, NULL}
};
@@ -13107,25 +13109,36 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
char timestr[100];
struct tm time_info;
bool tree = vshCommandOptBool(cmd, "tree");
+ const char *from = NULL;
+ virDomainSnapshotPtr start = NULL;
+
+ if (vshCommandOptString(cmd, "from", &from) < 0) {
+ vshError(ctl, _("invalid from argument '%s'"), from);
+ goto cleanup;
+ }
if (vshCommandOptBool(cmd, "parent")) {
if (vshCommandOptBool(cmd, "roots")) {
vshError(ctl, "%s",
- _("--parent and --roots are mutually exlusive"));
+ _("--parent and --roots are mutually exclusive"));
return false;
}
if (tree) {
vshError(ctl, "%s",
- _("--parent and --tree are mutually exlusive"));
+ _("--parent and --tree are mutually exclusive"));
return false;
}
parent_filter = 1;
} else if (vshCommandOptBool(cmd, "roots")) {
if (tree) {
vshError(ctl, "%s",
- _("--roots and --tree are mutually exlusive"));
+ _("--roots and --tree are mutually exclusive"));
return false;
}
+ if (from) {
+ vshError(ctl, "%s",
+ _("--roots and --from are mutually exclusive"));
+ }
flags |= VIR_DOMAIN_SNAPSHOT_LIST_ROOTS;
}
@@ -13140,10 +13153,23 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
if (dom == NULL)
goto cleanup;
- numsnaps = virDomainSnapshotNum(dom, flags);
+ if (from) {
+ start = virDomainSnapshotLookupByName(dom, from, 0);
+ if (!start)
+ goto cleanup;
+ if (vshCommandOptBool(cmd, "descendants") || tree) {
+ flags |= VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS;
+ }
+ numsnaps = virDomainSnapshotNumChildren(start, flags);
+ if (tree)
+ numsnaps++;
+ } else {
+ numsnaps = virDomainSnapshotNum(dom, flags);
+ }
- /* Fall back to simulation if --roots was unsupported. */
- if (numsnaps < 0 && last_error->code == VIR_ERR_INVALID_ARG &&
+ /* Fall back to simulation if --roots was unsupported.
+ * XXX Is it worth emulating --from on older servers? */
+ if (numsnaps < 0 && last_error->code == VIR_ERR_INVALID_ARG &&
!from &&
(flags & VIR_DOMAIN_SNAPSHOT_LIST_ROOTS)) {
virFreeError(last_error);
last_error = NULL;
@@ -13175,14 +13201,32 @@ cmdSnapshotList(vshControl *ctl, const vshCmd *cmd)
if (VIR_ALLOC_N(names, numsnaps) < 0)
goto cleanup;
- actual = virDomainSnapshotListNames(dom, names, numsnaps, flags);
+ if (from) {
+ /* When mixing --from and --tree, we want to start the tree at the
+ * given snapshot. Without --tree, only list the children. */
+ if (tree) {
+ if (numsnaps)
+ actual = virDomainSnapshotListChildrenNames(start, names + 1,
+ numsnaps - 1,
+ flags);
+ if (actual >= 0) {
+ actual++;
+ names[0] = vshStrdup(ctl, from);
+ }
+ } else {
+ actual = virDomainSnapshotListChildrenNames(start, names,
+ numsnaps, flags);
+ }
+ } else {
+ actual = virDomainSnapshotListNames(dom, names, numsnaps, flags);
+ }
if (actual < 0)
goto cleanup;
if (tree) {
char indentBuf[INDENT_BUFLEN];
- char **parents = vshMalloc(ctl, sizeof(char *) * actual);
- for (i = 0; i < actual; i++) {
+ char **parents = vshCalloc(ctl, sizeof(char *), actual);
+ for (i = (from ? 1 : 0); i < actual; i++) {
/* free up memory from previous iterations of the loop */
if (snapshot)
virDomainSnapshotFree(snapshot);
@@ -13277,6 +13321,8 @@ cleanup:
VIR_FREE(state);
if (snapshot)
virDomainSnapshotFree(snapshot);
+ if (start)
+ virDomainSnapshotFree(start);
xmlXPathFreeContext(ctxt);
xmlFreeDoc(xml);
VIR_FREE(doc);
diff --git a/tools/virsh.pod b/tools/virsh.pod
index be81afc..7c91d75 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1969,7 +1969,7 @@ The editor used can be supplied by the C<$VISUAL> or
C<$EDITOR> environment
variables, and defaults to C<vi>.
=item B<snapshot-list> I<domain> [{I<--parent> | I<--roots> |
I<--tree>}]
-[I<--metadata>]
+[I<--metadata>] [[I<--from>] B<snapshot> [I<--descendants>]]
List all of the available snapshots for the given domain, defaulting
to show columns for the snapshot name, creation time, and domain state.
@@ -1980,6 +1980,13 @@ the list will be filtered to just snapshots that have no parents.
If I<--tree> is specified, the output will be in a tree format, listing
just snapshot names. These three options are mutually exclusive.
+If I<--from> is provided, filter the list to snapshots which are
+children of the given B<snapshot>. When used in isolation or with
+I<--parent>, the list is limited to direct children unless
+I<--descendants> is also present. When used with I<--tree>, the
+use of I<--descendants> is implied. This option is not compatible
+with I<--roots>.
+
If I<--metadata> is specified, the list will be filtered to just
snapshots that involve libvirt metadata, and thus would prevent
B<undefine> of a persistent domain, or be lost on B<destroy> of
--
1.7.4.4