snapshot: add virsh back-compat support for new filters

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.
This commit is contained in:
Eric Blake 2012-11-13 09:40:37 -07:00
parent e9028f4b73
commit 1d272e8f22

View File

@ -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,63 @@ cleanup:
return ret;
}
/* Helper function to filter snapshots according to status and
* location portion of flags. Returns 0 if filter excluded snapshot,
* 1 if snapshot is okay (or if snapshot is already NULL), and -1 on
* failure, with error already 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 1;
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) {
vshError(ctl, "%s", _("unable to perform snapshot filtering"));
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:
VIR_FREE(state);
xmlXPathFreeContext(ctxt);
xmlFreeDoc(xmldoc);
VIR_FREE(xml);
return ret;
}
/*
* "snapshot-info" command
*/
@ -883,7 +941,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 +954,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 +963,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 +1024,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 +1224,29 @@ 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:
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;