From 37bb0447bb5e585fd0171b30628abd313a0b1e64 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Wed, 23 May 2012 10:40:50 -0600 Subject: [PATCH] list: add virDomainListAllSnapshots API There was an inherent race between virDomainSnapshotNum() and virDomainSnapshotListNames(), where an additional snapshot could be created in the meantime, or where a snapshot could be deleted before converting the name back to a virDomainSnapshotPtr. It was also an awkward name: the function operates on domains, not domain snapshots. virDomainSnapshotListChildrenNames() suffered from the same inherent race, although its naming was nicer. This patch makes things nicer by grabbing a snapshot list atomically, in the format most useful to the user. * include/libvirt/libvirt.h.in (virDomainListAllSnapshots) (virDomainSnapshotListAllChildren): New declarations. * src/libvirt.c (virDomainSnapshotListNames) (virDomainSnapshotListChildrenNames): Add cross-references. (virDomainListAllSnapshots, virDomainSnapshotListAllChildren): New functions. * src/libvirt_public.syms (LIBVIRT_0.9.13): Export them. * src/driver.h (virDrvDomainListAllSnapshots) (virDrvDomainSnapshotListAllChildren): New callbacks. * python/generator.py (skip_function): Prepare for later hand-written versions. --- include/libvirt/libvirt.h.in | 13 ++- python/generator.py | 2 + src/driver.h | 12 +++ src/libvirt.c | 177 +++++++++++++++++++++++++++++++++-- src/libvirt_public.syms | 2 + 5 files changed, 196 insertions(+), 10 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 0504a1c384..024c4ec552 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3390,7 +3390,8 @@ char *virDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot, * * Flags valid for virDomainSnapshotNum(), * virDomainSnapshotListNames(), virDomainSnapshotNumChildren(), and - * virDomainSnapshotListChildrenNames(). Note that the interpretation + * virDomainSnapshotListChildrenNames(), virDomainListAllSnapshots(), + * and virDomainSnapshotListAllChildren(). Note that the interpretation * of flag (1<<0) depends on which function it is passed to; but serves * to toggle the per-call default of whether the listing is shallow or * recursive. Remaining bits come in groups; if all bits from a group are @@ -3423,6 +3424,11 @@ int virDomainSnapshotNum(virDomainPtr domain, unsigned int flags); int virDomainSnapshotListNames(virDomainPtr domain, char **names, int nameslen, unsigned int flags); +/* Get all snapshot objects for this domain */ +int virDomainListAllSnapshots(virDomainPtr domain, + virDomainSnapshotPtr **snaps, + unsigned int flags); + /* Return the number of child snapshots for this snapshot */ int virDomainSnapshotNumChildren(virDomainSnapshotPtr snapshot, unsigned int flags); @@ -3432,6 +3438,11 @@ int virDomainSnapshotListChildrenNames(virDomainSnapshotPtr snapshot, char **names, int nameslen, unsigned int flags); +/* Get all snapshot object children for this snapshot */ +int virDomainSnapshotListAllChildren(virDomainSnapshotPtr snapshot, + virDomainSnapshotPtr **snaps, + unsigned int flags); + /* Get a handle to a named snapshot */ virDomainSnapshotPtr virDomainSnapshotLookupByName(virDomainPtr domain, const char *name, diff --git a/python/generator.py b/python/generator.py index 0b6ac7c2ab..2dada6e174 100755 --- a/python/generator.py +++ b/python/generator.py @@ -454,6 +454,8 @@ skip_function = ( 'virSaveLastError', # We have our own python error wrapper 'virFreeError', # Only needed if we use virSaveLastError 'virConnectListAllDomains', #overridden in virConnect.py + 'virDomainListAllSnapshots', # overridden in virDomain.py + 'virDomainSnapshotListAllChildren', # overridden in virDomainSnapshot.py 'virStreamRecvAll', # Pure python libvirt-override-virStream.py 'virStreamSendAll', # Pure python libvirt-override-virStream.py diff --git a/src/driver.h b/src/driver.h index 09a8adf997..b3c1740a5e 100644 --- a/src/driver.h +++ b/src/driver.h @@ -627,6 +627,11 @@ typedef int int nameslen, unsigned int flags); +typedef int + (*virDrvDomainListAllSnapshots)(virDomainPtr domain, + virDomainSnapshotPtr **snaps, + unsigned int flags); + typedef int (*virDrvDomainSnapshotNumChildren)(virDomainSnapshotPtr snapshot, unsigned int flags); @@ -637,6 +642,11 @@ typedef int int nameslen, unsigned int flags); +typedef int + (*virDrvDomainSnapshotListAllChildren)(virDomainSnapshotPtr snapshot, + virDomainSnapshotPtr **snaps, + unsigned int flags); + typedef virDomainSnapshotPtr (*virDrvDomainSnapshotLookupByName)(virDomainPtr domain, const char *name, @@ -993,8 +1003,10 @@ struct _virDriver { virDrvDomainSnapshotGetXMLDesc domainSnapshotGetXMLDesc; virDrvDomainSnapshotNum domainSnapshotNum; virDrvDomainSnapshotListNames domainSnapshotListNames; + virDrvDomainListAllSnapshots domainListAllSnapshots; virDrvDomainSnapshotNumChildren domainSnapshotNumChildren; virDrvDomainSnapshotListChildrenNames domainSnapshotListChildrenNames; + virDrvDomainSnapshotListAllChildren domainSnapshotListAllChildren; virDrvDomainSnapshotLookupByName domainSnapshotLookupByName; virDrvDomainHasCurrentSnapshot domainHasCurrentSnapshot; virDrvDomainSnapshotGetParent domainSnapshotGetParent; diff --git a/src/libvirt.c b/src/libvirt.c index 748791f330..0aa50cb6e9 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -17123,9 +17123,8 @@ error: * @flags: bitwise-OR of supported virDomainSnapshotListFlags * * Collect the list of domain snapshots for the given domain, and store - * their names in @names. Caller is responsible for freeing each member - * of the array. The value to use for @nameslen can be determined by - * virDomainSnapshotNum() with the same @flags. + * their names in @names. The value to use for @nameslen can be determined + * by virDomainSnapshotNum() with the same @flags. * * By default, this command covers all snapshots; it is also possible to * limit things to just snapshots with no parents, when @flags includes @@ -17149,14 +17148,17 @@ error: * whether they have metadata that would prevent the removal of the last * reference to a domain. * - * Returns the number of domain snapshots found or -1 in case of error. * Note that this command is inherently racy: another connection can * define a new snapshot between a call to virDomainSnapshotNum() and * this call. You are only guaranteed that all currently defined * snapshots were listed if the return is less than @nameslen. Likewise, * you should be prepared for virDomainSnapshotLookupByName() to fail when * converting a name from this call into a snapshot object, if another - * connection deletes the snapshot in the meantime. + * connection deletes the snapshot in the meantime. For more control over + * the results, see virDomainListAllSnapshots(). + * + * Returns the number of domain snapshots found or -1 in case of error. + * The caller is responsible for freeing each member of the array. */ int virDomainSnapshotListNames(virDomainPtr domain, char **names, int nameslen, @@ -17194,6 +17196,81 @@ error: return -1; } +/** + * virDomainListAllSnapshots: + * @domain: a domain object + * @snaps: pointer to variable to store the array containing snapshot objects, + * or NULL if the list is not required (just returns number of + * snapshots) + * @flags: bitwise-OR of supported virDomainSnapshotListFlags + * + * Collect the list of domain snapshots for the given domain, and allocate + * an array to store those objects. This API solves the race inherent in + * virDomainSnapshotListNames(). + * + * By default, this command covers all snapshots; it is also possible to + * limit things to just snapshots with no parents, when @flags includes + * VIR_DOMAIN_SNAPSHOT_LIST_ROOTS. Additional filters are provided in + * groups, where each group contains bits that describe mutually exclusive + * attributes of a snapshot, and where all bits within a group describe + * all possible snapshots. Some hypervisors might reject explicit bits + * from a group where the hypervisor cannot make a distinction. For a + * group supported by a given hypervisor, the behavior when no bits of a + * group are set is identical to the behavior when all bits in that group + * are set. When setting bits from more than one group, it is possible to + * select an impossible combination, in that case a hypervisor may return + * either 0 or an error. + * + * The first group of @flags is VIR_DOMAIN_SNAPSHOT_LIST_LEAVES and + * VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES, to filter based on snapshots that + * have no further children (a leaf snapshot). + * + * The next group of @flags is VIR_DOMAIN_SNAPSHOT_LIST_METADATA and + * VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA, for filtering snapshots based on + * whether they have metadata that would prevent the removal of the last + * reference to a domain. + * + * Returns the number of domain snapshots found or -1 and sets @snaps to + * NULL in case of error. On success, the array stored into @snaps is + * guaranteed to have an extra allocated element set to NULL but not included + * in the return count, to make iteration easier. The caller is responsible + * for calling virDomainSnapshotFree() on each array element, then calling + * free() on @snaps. + */ +int +virDomainListAllSnapshots(virDomainPtr domain, virDomainSnapshotPtr **snaps, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "snaps=%p, flags=%x", snaps, flags); + + virResetLastError(); + + if (snaps) + *snaps = NULL; + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = domain->conn; + + if (conn->driver->domainListAllSnapshots) { + int ret = conn->driver->domainListAllSnapshots(domain, snaps, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + /** * virDomainSnapshotNumChildren: * @snapshot: a domain snapshot object @@ -17263,9 +17340,9 @@ error: * @flags: bitwise-OR of supported virDomainSnapshotListFlags * * Collect the list of domain snapshots that are children of the given - * snapshot, and store their names in @names. Caller is responsible for - * freeing each member of the array. The value to use for @nameslen can - * be determined by virDomainSnapshotNumChildren() with the same @flags. + * snapshot, and store their names in @names. The value to use for + * @nameslen can be determined by virDomainSnapshotNumChildren() with + * the same @flags. * * By default, this command covers only direct children; it is also possible * to expand things to cover all descendants, when @flags includes @@ -17296,7 +17373,11 @@ error: * snapshots were listed if the return is less than @nameslen. Likewise, * you should be prepared for virDomainSnapshotLookupByName() to fail when * converting a name from this call into a snapshot object, if another - * connection deletes the snapshot in the meantime. + * connection deletes the snapshot in the meantime. For more control over + * the results, see virDomainSnapshotListAllChildren(). + * + * Returns the number of domain snapshots found or -1 in case of error. + * The caller is responsible for freeing each member of the array. */ int virDomainSnapshotListChildrenNames(virDomainSnapshotPtr snapshot, @@ -17338,6 +17419,84 @@ error: return -1; } +/** + * virDomainSnapshotListAllChildren: + * @snapshot: a domain snapshot object + * @snaps: pointer to variable to store the array containing snapshot objects, + * or NULL if the list is not required (just returns number of + * snapshots) + * @flags: bitwise-OR of supported virDomainSnapshotListFlags + * + * Collect the list of domain snapshots that are children of the given + * snapshot, and allocate an array to store those objects. This API solves + * the race inherent in virDomainSnapshotListChildrenNames(). + * + * By default, this command covers only direct children; it is also possible + * to expand things to cover all descendants, when @flags includes + * VIR_DOMAIN_SNAPSHOT_LIST_DESCENDANTS. Also, some filters are provided in + * groups, where each group contains bits that describe mutually exclusive + * attributes of a snapshot, and where all bits within a group describe + * all possible snapshots. Some hypervisors might reject explicit bits + * from a group where the hypervisor cannot make a distinction. For a + * group supported by a given hypervisor, the behavior when no bits of a + * group are set is identical to the behavior when all bits in that group + * are set. When setting bits from more than one group, it is possible to + * select an impossible combination, in that case a hypervisor may return + * either 0 or an error. + * + * The first group of @flags is VIR_DOMAIN_SNAPSHOT_LIST_LEAVES and + * VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES, to filter based on snapshots that + * have no further children (a leaf snapshot). + * + * The next group of @flags is VIR_DOMAIN_SNAPSHOT_LIST_METADATA and + * VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA, for filtering snapshots based on + * whether they have metadata that would prevent the removal of the last + * reference to a domain. + * + * Returns the number of domain snapshots found or -1 and sets @snaps to + * NULL in case of error. On success, the array stored into @snaps is + * guaranteed to have an extra allocated element set to NULL but not included + * in the return count, to make iteration easier. The caller is responsible + * for calling virDomainSnapshotFree() on each array element, then calling + * free() on @snaps. + */ +int +virDomainSnapshotListAllChildren(virDomainSnapshotPtr snapshot, + virDomainSnapshotPtr **snaps, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DEBUG("snapshot=%p, snaps=%p, flags=%x", snapshot, snaps, flags); + + virResetLastError(); + + if (snaps) + *snaps = NULL; + + if (!VIR_IS_DOMAIN_SNAPSHOT(snapshot)) { + virLibDomainSnapshotError(VIR_ERR_INVALID_DOMAIN_SNAPSHOT, + __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = snapshot->domain->conn; + + if (conn->driver->domainSnapshotListAllChildren) { + int ret = conn->driver->domainSnapshotListAllChildren(snapshot, snaps, + flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + /** * virDomainSnapshotLookupByName: * @domain: a domain object diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index ea49a68dbf..2913a81909 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -537,8 +537,10 @@ LIBVIRT_0.9.11 { LIBVIRT_0.9.13 { global: virConnectListAllDomains; + virDomainListAllSnapshots; virDomainSnapshotHasMetadata; virDomainSnapshotIsCurrent; + virDomainSnapshotListAllChildren; virDomainSnapshotRef; } LIBVIRT_0.9.11;