Snapshot filtering based on types is useful enough to add
back-compat support into virsh. It is also rather easy - all
versions of libvirt that don't understand the new filter flags
already gave us sufficient information in a single XML field
to reconstruct all the information we need (that is, it isn't
until libvirt 1.0.1 that we have more interesting types of
snapshots, such as offline external).
* tools/virsh-snapshot.c (vshSnapshotFilter): New function.
(vshSnapshotListCollect): Add fallback support.
---
tools/virsh-snapshot.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 110 insertions(+), 1 deletion(-)
diff --git a/tools/virsh-snapshot.c b/tools/virsh-snapshot.c
index 0363e19..7cd2966 100644
--- a/tools/virsh-snapshot.c
+++ b/tools/virsh-snapshot.c
@@ -39,6 +39,7 @@
#include "util.h"
#include "virsh-domain.h"
#include "xml.h"
+#include "conf/snapshot_conf.h"
/* Helper for snapshot-create and snapshot-create-as */
static bool
@@ -712,6 +713,67 @@ cleanup:
return ret;
}
+/* Helper function to filter snapshots according to status and
+ * location portion of flags. Returns 0 if filter excluded snapshot
+ * (or if snapshot is already NULL), 1 if snapshot is okay, and -1 on
+ * failure with error reported. */
+static int
+vshSnapshotFilter(vshControl *ctl, virDomainSnapshotPtr snapshot,
+ unsigned int flags)
+{
+ char *xml = NULL;
+ xmlDocPtr xmldoc = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ int ret = -1;
+ char *state = NULL;
+
+ if (!snapshot)
+ return 0;
+
+ xml = virDomainSnapshotGetXMLDesc(snapshot, 0);
+ if (!xml)
+ goto cleanup;
+
+ xmldoc = virXMLParseStringCtxt(xml, _("(domain_snapshot)"), &ctxt);
+ if (!xmldoc)
+ goto cleanup;
+
+ /* Libvirt 1.0.1 and newer never call this function, because the
+ * filtering is already supported by the listing functions. Older
+ * libvirt lacked /domainsnapshot/memory, but was also limited in
+ * the types of snapshots it could create: if state was disk-only,
+ * the snapshot is external; all other snapshots are internal. */
+ state = virXPathString("string(/domainsnapshot/state)", ctxt);
+ if (!state)
+ goto cleanup;
+ if (STREQ(state, "disk-snapshot")) {
+ ret = ((flags & (VIR_DOMAIN_SNAPSHOT_LIST_DISK_ONLY |
+ VIR_DOMAIN_SNAPSHOT_LIST_EXTERNAL)) ==
+ (VIR_DOMAIN_SNAPSHOT_LIST_DISK_ONLY |
+ VIR_DOMAIN_SNAPSHOT_LIST_EXTERNAL));
+ } else {
+ if (!(flags & VIR_DOMAIN_SNAPSHOT_LIST_INTERNAL))
+ ret = 0;
+ else if (STREQ(state, "shutoff"))
+ ret = !!(flags & VIR_DOMAIN_SNAPSHOT_LIST_INACTIVE);
+ else
+ ret = !!(flags & VIR_DOMAIN_SNAPSHOT_LIST_ACTIVE);
+ }
+
+cleanup:
+ if (ret < 0) {
+ vshReportError(ctl);
+ vshError(ctl, "%s", _("unable to perform snapshot
filtering"));
+ } else {
+ vshResetLibvirtError();
+ }
+ VIR_FREE(state);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(xmldoc);
+ VIR_FREE(xml);
+ return ret;
+}
+
/*
* "snapshot-info" command
*/
@@ -883,7 +945,7 @@ vshSnapSorter(const void *a, const void *b)
static vshSnapshotListPtr
vshSnapshotListCollect(vshControl *ctl, virDomainPtr dom,
virDomainSnapshotPtr from,
- unsigned int flags, bool tree)
+ unsigned int orig_flags, bool tree)
{
int i;
char **names = NULL;
@@ -896,6 +958,8 @@ vshSnapshotListCollect(vshControl *ctl, virDomainPtr dom,
const char *fromname = NULL;
int start_index = -1;
int deleted = 0;
+ bool filter_fallback = false;
+ unsigned int flags = orig_flags;
/* Try the interface available in 0.9.13 and newer. */
if (!ctl->useSnapshotOld) {
@@ -903,6 +967,20 @@ vshSnapshotListCollect(vshControl *ctl, virDomainPtr dom,
count = virDomainSnapshotListAllChildren(from, &snaps, flags);
else
count = virDomainListAllSnapshots(dom, &snaps, flags);
+ /* If we failed because of flags added in 1.0.1, we can do
+ * fallback filtering. */
+ if (count < 0 && last_error->code == VIR_ERR_INVALID_ARG
&&
+ flags & (VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS |
+ VIR_DOMAIN_SNAPSHOT_FILTERS_LOCATION)) {
+ flags &= ~(VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS |
+ VIR_DOMAIN_SNAPSHOT_FILTERS_LOCATION);
+ vshResetLibvirtError();
+ filter_fallback = true;
+ if (from)
+ count = virDomainSnapshotListAllChildren(from, &snaps, flags);
+ else
+ count = virDomainListAllSnapshots(dom, &snaps, flags);
+ }
}
if (count >= 0) {
/* When mixing --from and --tree, we also want a copy of from
@@ -950,6 +1028,12 @@ vshSnapshotListCollect(vshControl *ctl, virDomainPtr dom,
return snaplist;
flags &= ~VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA;
}
+ if (flags & (VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS |
+ VIR_DOMAIN_SNAPSHOT_FILTERS_LOCATION)) {
+ flags &= ~(VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS |
+ VIR_DOMAIN_SNAPSHOT_FILTERS_LOCATION);
+ filter_fallback = true;
+ }
/* This uses the interfaces available in 0.8.0-0.9.6
* (virDomainSnapshotListNames, global list only) and in
@@ -1144,6 +1228,31 @@ vshSnapshotListCollect(vshControl *ctl, virDomainPtr dom,
}
success:
+ if (filter_fallback) {
+ /* Older API didn't filter on status or location, but the
+ * information is available in domain XML. */
+ if (!(orig_flags & VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS))
+ orig_flags |= VIR_DOMAIN_SNAPSHOT_FILTERS_STATUS;
+ if (!(orig_flags & VIR_DOMAIN_SNAPSHOT_FILTERS_LOCATION))
+ orig_flags |= VIR_DOMAIN_SNAPSHOT_FILTERS_LOCATION;
+ for (i = 0; i < snaplist->nsnaps; i++) {
+ switch (vshSnapshotFilter(ctl, snaplist->snaps[i].snap,
+ orig_flags)) {
+ case 1:
+ break;
+ case 0:
+ if (snaplist->snaps[i].snap) {
+ virDomainSnapshotFree(snaplist->snaps[i].snap);
+ snaplist->snaps[i].snap = NULL;
+ VIR_FREE(snaplist->snaps[i].parent);
+ deleted++;
+ }
+ break;
+ default:
+ goto cleanup;
+ }
+ }
+ }
qsort(snaplist->snaps, snaplist->nsnaps, sizeof(*snaplist->snaps),
vshSnapSorter);
snaplist->nsnaps -= deleted;
--
1.7.11.7