[libvirt] RFE: virConnectListAllDomains()

Daniel P. Berrange berrange at redhat.com
Mon May 21 08:33:26 UTC 2012


On Fri, May 18, 2012 at 05:52:36PM -0600, Eric Blake wrote:
> Use of virConnectListDomains() and virConnectListDefinedDomains() is:
> 
> 1. inherently racy.  A domain can change between active and inactive
> between two back-to-back calls, and thus be entirely skipped or
> enumerated twice when concatenating lists.
> 
> 2. painful to use.  ListDomains gives ids, ListDefinedDomains gives
> names, and the user must then call virDomainLookupByID() and
> virDomainLookupByName() to convert into UUIDs.
> 
> 3. requires pre-allocation.  The user must call virConnectNumOfDomains()
> then over-allocate before calling virConnectListDomains(), in order to
> guarantee that the list size didn't change between the two calls.

Likewise virConnectList{StoragePools,Secrets,Networks,Interfaces,NodeDevices}
and virStoragePoolListVolumes

> 
> This is a proposal for a new API that addresses all three points - by
> returning virDomainPtr rather than id or strings, the UUID of each
> domain can be grabbed in one shot.  By consolidating things into a
> single API call, there is no race in trying to piece together the
> complete list.  By having libvirt allocate the resulting array, rather
> than making the caller pre-allocate, the user doesn't have to worry
> about a race between getting a count and using that count.  It also
> provides the convenience of returning smaller lists based on various
> filtering groups.
> 
> Thoughts before I expand this API and add the actual implementation?

This was one of the items on my "TODO" list that I considered
neccessary before a 1.0 release of libvirt. IF we do this, we
should of course also do the same for the other similar APIs
we have with race conditions.

> +/**
> + * virConnectListAllDomainsFlags:
> + *
> + * Flags used to tune which domains are listed by
> virConnectListAllDomains().
> + * Note that these flags come in groups; if all bits from a group are 0,
> + * then that group is not used to filter results.

Hmm, interesting idea.

> + */
> +typedef enum {
> +    VIR_CONNECT_LIST_DOMAINS_ACTIVE         = 1 << 0,
> +    VIR_CONNECT_LIST_DOMAINS_INACTIVE       = 1 << 1,
> +
> +    VIR_CONNECT_LIST_DOMAINS_PERSISTENT     = 1 << 2,
> +    VIR_CONNECT_LIST_DOMAINS_TRANSIENT      = 1 << 3,
> +
> +    VIR_CONNECT_LIST_DOMAINS_RUNNING        = 1 << 4,
> +    VIR_CONNECT_LIST_DOMAINS_PAUSED         = 1 << 5,
> +    VIR_CONNECT_LIST_DOMAINS_SHUTOFF        = 1 << 6,
> +    VIR_CONNECT_LIST_DOMAINS_OTHER          = 1 << 7,
> +
> +    VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE    = 1 << 8,
> +    VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE = 1 << 9,
> +
> +    VIR_CONNECT_LIST_DOMAINS_AUTOSTART      = 1 << 10,
> +    VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART   = 1 << 11,
> +
> +    VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT   = 1 << 12,
> +    VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT    = 1 << 13,
> +} virConnectListAllDomainsFlags;
> +
> +int                     virConnectListAllDomains(virConnectPtr conn,
> +                                                 virDomainPtr *doms,
> +                                                 unsigned int flags);
> 
>  /*
>   * Get connection from domain.
> diff --git i/src/libvirt.c w/src/libvirt.c
> index 22fc863..9d7d96f 100644
> --- i/src/libvirt.c
> +++ w/src/libvirt.c
> @@ -1859,7 +1859,14 @@ error:
>   *
>   * Collect the list of active domains, and store their IDs in array @ids
>   *
> - * Returns the number of domains found or -1 in case of error
> + * For inactive domains, see virConnectListDefinedDomains().  For more
> + * control over the results, see virConnectListAllDomains().
> + *
> + * Returns the number of domains found or -1 in case of error.  Note that
> + * this command is inherently racy; a domain can be started between a
> + * call to virConnectNumOfDomains() and this call; you are only guaranteed
> + * that all currently active domains were listed if the return is less
> + * than @maxids.
>   */
>  int
>  virConnectListDomains(virConnectPtr conn, int *ids, int maxids)
> @@ -1927,6 +1934,88 @@ error:
>  }
> 
>  /**
> + * virConnectListAllDomains:
> + * @conn: pointer to the hypervisor connection
> + * @doms: pointer to the location to store allocated output array
> + * @flags: bitwise-OR of virConnectListAllDomainsFlags
> + *
> + * Collect a possibly-filtered list of all domains, and return an allocated
> + * array of information for each.  This API solves the race inherent in
> + * virConnectListDomains() and virConnectListDefinedDomains().
> + *
> + * Normally, all domains are returned; however, @flags can be used to
> + * filter the results for a smaller list of targetted domains.  The valid
> + * flags are divided into groups, where each group contains bits that
> + * describe mutually exclusive attributes of a domain, and where all bits
> + * within a group describe all possible domains.  Some hypervisors might
> + * reject explicit bits from a group where the hypervisor cannot make a
> + * distinction (for example, not all hypervisors can tell whether domains
> + * have snapshots).  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 (such
> + * as an inactive transient domain), in that case a hypervisor may return
> + * either 0 or an error.
> + *
> + * The first group of @flags is VIR_CONNECT_LIST_DOMAINS_ACTIVE (online
> + * domains) and VIR_CONNECT_LIST_DOMAINS_INACTIVE (offline domains).
> + *
> + * The next group of @flags is VIR_CONNECT_LIST_DOMAINS_PERSISTENT (defined
> + * domains) and VIR_CONNECT_LIST_DOMAINS_TRANSIENT (running but not
> defined).
> + *
> + * The next group of @flags covers various domain states:
> + * VIR_CONNECT_LIST_DOMAINS_RUNNING, VIR_CONNECT_LIST_DOMAINS_PAUSED,
> + * VIR_CONNECT_LIST_DOMAINS_SHUTOFF, and a catch-all for all other states
> + * (such as crashed, this catch-all covers the possibility of adding new
> + * states).
> + *
> + * The remaining groups cover boolean attributes commonly asked about
> + * domains; they include VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE and
> + * VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE, for filtering based on whether
> + * a managed save image exists; VIR_CONNECT_LIST_DOMAINS_AUTOSTART and
> + * VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART, for filtering based on autostart;
> + * VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT and
> + * VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT, for filtering based on whether
> + * a domain has snapshots.
> + *
> + * Returns the number of domains found or -1 in case of error.  On success,
> + * the array stored into @doms is guaranteed to have an extra allocated
> + * element set to NULL, to make iteration easier.  The caller is
> + * responsible for calling virDomainFree() on each array element, then
> + * calling free() on @doms.
> + */
> +int
> +virConnectListDomains(virConnectPtr conn, int *ids, int maxids)

The signature is wrong here, but I know what you intended

> +{
> +    VIR_DEBUG("conn=%p, ids=%p, maxids=%d", conn, ids, maxids);
> +
> +    virResetLastError();
> +
> +    if (!VIR_IS_CONNECT(conn)) {
> +        virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__);
> +        virDispatchError(NULL);
> +        return -1;
> +    }
> +
> +    if ((ids == NULL) || (maxids < 0)) {
> +        virLibConnError(VIR_ERR_INVALID_ARG, __FUNCTION__);
> +        goto error;
> +    }
> +
> +    if (conn->driver->listDomains) {
> +        int ret = conn->driver->listDomains (conn, ids, maxids);
> +        if (ret < 0)
> +            goto error;
> +        return ret;
> +    }
> +
> +    virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__);
> +error:
> +    virDispatchError(conn);
> +    return -1;
> +}
> +
> +/**
>   * virDomainGetConnect:
>   * @dom: pointer to a domain
>   *
> @@ -8092,7 +8181,14 @@ error:
>   * list the defined but inactive domains, stores the pointers to the names
>   * in @names
>   *
> - * Returns the number of names provided in the array or -1 in case of error
> + * For active domains, see virConnectListDomains().  For more control over
> + * the results, see virConnectListAllDomains().
> + *
> + * Returns the number of names provided in the array or -1 in case of
> error.
> + * Note that this command is inherently racy; a domain can be defined
> between
> + * a call to virConnectNumOfDefinedDomains() and this call; you are only
> + * guaranteed that all currently defined domains were listed if the return
> + * is less than @maxids.  The client must call free() on each returned
> name.
>   */
>  int
>  virConnectListDefinedDomains(virConnectPtr conn, char **const names,


This gets my vote

Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list