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.
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?
diff --git i/include/libvirt/libvirt.h.in w/include/libvirt/libvirt.h.in
index a817db8..ea63d9f 100644
--- i/include/libvirt/libvirt.h.in
+++ w/include/libvirt/libvirt.h.in
@@ -1192,6 +1192,38 @@ int virConnectListDomains
(virConnectPtr conn,
*/
int virConnectNumOfDomains (virConnectPtr conn);
+/**
+ * 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.
+ */
+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)
+{
+ 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,
--
Eric Blake eblake(a)redhat.com +1-919-301-3266
Libvirt virtualization library
http://libvirt.org