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 bc9ffc7..d5c71be 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_OFFLINE);
+        else
+            ret = !!(flags & VIR_DOMAIN_SNAPSHOT_LIST_ONLINE);
+    }
+
+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

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to