[libvirt] [PATCH 00/45 v3] Atomic APIs to list objects

v3 - v4: * Except a few patches are pushed, it's just rebasing on the top v2 - v3: * Various document fixes/improvements (Suggested by Laine and Eric) * Destroy virdomainlist.[ch], and folder all list helper funcs into its own *_conf.[ch] * Improve the helpers to get the object list. See https://www.redhat.com/archives/libvir-list/2012-July/msg01267.html * Rebased on top of commit 3df9626 by Peter and the virReportError series by Daniel. * Rebased on top of virsh split series. (I'm luck there is no change on those commands since v2). Except the already supported APIs for domain and domain snapshot, this series add the APIs for the left objects, including storage pool, storage vol, network, interface, node device, nwfilter, and secret. * Storage pool: - Support filtering the returned pool objects by active|inactive, persistent|transient, autostart|no-autostart, and pool types. - New options for virsh, --type to accept multiple pool types. * Storage vol: - Simply returns all the vol objects of a pool. * Network: - Support filtering the results using flags active|inactive, persistent|transient, autostart|no-autostart - New options for virsh. * Interface: - Support filtering the results using flags active|inactive. It's still O(n) underlying, as interface driver doesn't manage the objects itself, but using netcf lib instead. And netcf APIs don't support returning the struct yet. * Node Device: - Support filtering the results using capabilities type of the devices. - Extend --cap to accept multiple capability type. * Network Filter: - Simply returns all the objects. * Secret: - Simply returns all the objects. Osier Yang (45): list: Define new API virStorageListAllStoragePools list: Add helpers for listing storage pool objects list: Implement the RPC calls for virConnectListAllStoragePools list: Implement listAllStoragePools for storage driver list: Implement listAllStoragePools for test driver list: Add helper to convert strings separated by ', ' to array virsh: Fix the wrong doc for pool-list list: Change MATCH for common use in virsh list: Use virConnectListAllStoragePools in virsh python: Expose virStorageListAllStoragePools to python binding list: Define new API virStoragePoolListAllVolumes list: Implemente RPC calls for virStoragePoolListAllVolumes list: Implement virStoragePoolListAllVolumes for storage driver list: Implement virStoragePoolListAllVolumes for test driver list: Use virStoragePoolListAllVolumes in virsh list: Expose virStoragePoolListAllVolumes to Python binding list: Define new API virConnectListAllNetworks list: Implement RPC calls for virConnectListAllNetworks list: Add helpers to list network objects list: Implement listAllNetworks for network driver list: Implement listAllNetworks for test driver list: Use virConnectListAllNetworks in virsh list: Expose virConnectListAllNetworks to Python binding list: Define new API virConnectListAllInterfaces list: Implemente RPC calls for virConnectListAllInterfaces list: Implement listAllInterfaces list: Use virConnectListAllInterfaces in virsh list: Expose virConnectListAllInterfaces to Python binding list: Define new API virConnectListAllNodeDevices list: Implemente RPC calls for virConnectListAllNodeDevices list: Add helpers for listing node devices list: Implement listAllNodeDevices list: Expose virConnectListAllNodeDevices to Python binding virsh: Fix a bug of nodedev-list list: Use virConnectListAllNodeDevices in virsh list: Define new API virConnectListAllNWFilters list: Implement RPC calls for virConnectListAllNWFilters list: Implement listAllNWFilters list: Use virConnectListAllNWFilters in virsh list: Expose virConnectListAllNWFilters to Python binding list: Define new API virConnectListAllSecrets list: Implement RPC calls for virConnectListAllSecrets list: Implement listAllSecrets list: Use virConnectListAllSecrets in virsh list: Expose virConnectListAllSecrets to Python binding daemon/remote.c | 382 ++++++++++++++++++++++ include/libvirt/libvirt.h.in | 101 ++++++- python/generator.py | 11 +- python/libvirt-override-api.xml | 44 +++- python/libvirt-override-virConnect.py | 72 +++++ python/libvirt-override-virStoragePool.py | 11 + python/libvirt-override.c | 337 ++++++++++++++++++++ src/conf/network_conf.c | 91 ++++++ src/conf/network_conf.h | 22 ++ src/conf/node_device_conf.c | 103 ++++++ src/conf/node_device_conf.h | 16 + src/conf/storage_conf.c | 116 +++++++ src/conf/storage_conf.h | 35 ++ src/driver.h | 35 ++- src/interface/netcf_driver.c | 135 ++++++++ src/libvirt.c | 487 ++++++++++++++++++++++++++++- src/libvirt_private.syms | 4 + src/libvirt_public.syms | 7 + src/network/bridge_driver.c | 17 + src/node_device/node_device_driver.c | 15 + src/node_device/node_device_driver.h | 3 + src/node_device/node_device_hal.c | 1 + src/node_device/node_device_udev.c | 1 + src/nwfilter/nwfilter_driver.c | 56 ++++ src/remote/remote_driver.c | 449 ++++++++++++++++++++++++++ src/remote/remote_protocol.x | 80 +++++- src/remote_protocol-structs | 85 +++++ src/secret/secret_driver.c | 59 ++++- src/storage/storage_driver.c | 85 +++++ src/test/test_driver.c | 101 ++++++ tools/virsh-domain-monitor.c | 2 - tools/virsh-domain.c | 19 +- tools/virsh-interface.c | 259 +++++++++++----- tools/virsh-network.c | 351 ++++++++++++++++----- tools/virsh-nodedev.c | 302 ++++++++++++++++--- tools/virsh-nwfilter.c | 163 ++++++++-- tools/virsh-pool.c | 441 ++++++++++++++++++++------ tools/virsh-secret.c | 182 +++++++++-- tools/virsh-volume.c | 197 +++++++++--- tools/virsh.c | 56 +++- tools/virsh.pod | 51 +++- 41 files changed, 4521 insertions(+), 463 deletions(-) create mode 100644 python/libvirt-override-virStoragePool.py -- 1.7.7.3

This introduces a new API to list the storage pool objects, 4 groups of flags are provided to filter the returned pools: * Active or not * Autostarting or not * Persistent or not * And the pool type. include/libvirt/libvirt.h.in: New enum virConnectListAllStoragePoolFlags; Declare the API. python/generator.py: Skip the generating src/driver.h: (virDrvConnectListAllStoragePools) src/libvirt.c: Implementation for the API. src/libvirt_public.syms: Export the symbol. --- include/libvirt/libvirt.h.in | 33 ++++++++++++ python/generator.py | 5 +- src/driver.h | 5 ++ src/libvirt.c | 112 +++++++++++++++++++++++++++++++++++++++--- src/libvirt_public.syms | 1 + 5 files changed, 146 insertions(+), 10 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 91e0a29..1e17207 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2524,6 +2524,39 @@ int virConnectListDefinedStoragePools(virConnectPtr conn, int maxnames); /* + * virConnectListAllStoragePoolsFlags: + * + * Flags used to tune pools returned by virConnectListAllStoragePools(). + * 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_STORAGE_POOLS_INACTIVE = 1 << 0, + VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE = 1 << 1, + + VIR_CONNECT_LIST_STORAGE_POOLS_PERSISTENT = 1 << 2, + VIR_CONNECT_LIST_STORAGE_POOLS_TRANSIENT = 1 << 3, + + VIR_CONNECT_LIST_STORAGE_POOLS_AUTOSTART = 1 << 4, + VIR_CONNECT_LIST_STORAGE_POOLS_NO_AUTOSTART = 1 << 5, + + /* List pools by type */ + VIR_CONNECT_LIST_STORAGE_POOLS_DIR = 1 << 6, + VIR_CONNECT_LIST_STORAGE_POOLS_FS = 1 << 7, + VIR_CONNECT_LIST_STORAGE_POOLS_NETFS = 1 << 8, + VIR_CONNECT_LIST_STORAGE_POOLS_LOGICAL = 1 << 9, + VIR_CONNECT_LIST_STORAGE_POOLS_DISK = 1 << 10, + VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI = 1 << 11, + VIR_CONNECT_LIST_STORAGE_POOLS_SCSI = 1 << 12, + VIR_CONNECT_LIST_STORAGE_POOLS_MPATH = 1 << 13, + VIR_CONNECT_LIST_STORAGE_POOLS_RBD = 1 << 14, + VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG = 1 << 15, +} virConnectListAllStoragePoolsFlags; + +int virConnectListAllStoragePools(virConnectPtr conn, + virStoragePoolPtr **pools, + unsigned int flags); +/* * Query a host for storage pools of a particular type */ char * virConnectFindStoragePoolSources(virConnectPtr conn, diff --git a/python/generator.py b/python/generator.py index 6559ece..6fab68c 100755 --- a/python/generator.py +++ b/python/generator.py @@ -337,7 +337,7 @@ foreign_encoding_args = ( # ####################################################################### -# Class methods which are written by hand in libvir.c but the Python-level +# Class methods which are written by hand in libvirt.c but the Python-level # code is still automatically generated (so they are not in skip_function()). skip_impl = ( 'virConnectGetVersion', @@ -455,9 +455,10 @@ skip_function = ( 'virConnectDomainEventDeregisterAny', # overridden in virConnect.py 'virSaveLastError', # We have our own python error wrapper 'virFreeError', # Only needed if we use virSaveLastError - 'virConnectListAllDomains', #overridden in virConnect.py + 'virConnectListAllDomains', # overridden in virConnect.py 'virDomainListAllSnapshots', # overridden in virDomain.py 'virDomainSnapshotListAllChildren', # overridden in virDomainSnapshot.py + 'virConnectListAllStoragePools', # overridden in virConnect.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 aab9766..3c1ae3b 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1232,6 +1232,10 @@ typedef int (*virDrvConnectListDefinedStoragePools) (virConnectPtr conn, char **const names, int maxnames); +typedef int + (*virDrvConnectListAllStoragePools) (virConnectPtr conn, + virStoragePoolPtr **pools, + unsigned int flags); typedef char * (*virDrvConnectFindStoragePoolSources) (virConnectPtr conn, const char *type, @@ -1376,6 +1380,7 @@ struct _virStorageDriver { virDrvConnectListStoragePools listPools; virDrvConnectNumOfDefinedStoragePools numOfDefinedPools; virDrvConnectListDefinedStoragePools listDefinedPools; + virDrvConnectListAllStoragePools listAllPools; virDrvConnectFindStoragePoolSources findPoolSources; virDrvStoragePoolLookupByName poolLookupByName; virDrvStoragePoolLookupByUUID poolLookupByUUID; diff --git a/src/libvirt.c b/src/libvirt.c index 893d380..82f5956 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -11218,6 +11218,90 @@ virStoragePoolGetConnect (virStoragePoolPtr pool) } /** + * virConnectListAllStoragePools: + * @conn: Pointer to the hypervisor connection. + * @pools: Pointer to a variable to store the array containing storage pool + * objects or NULL if the list is not required (just returns number + * of pools). + * @flags: bitwise-OR of virConnectListAllStoragePoolsFlags. + * + * Collect the list of storage pools, and allocate an array to store those + * objects. This API solves the race inherent between + * virConnectListStoragePools and virConnectListDefinedStoragePools. + * + * Normally, all storage pools are returned; however, @flags can be used to + * filter the results for a smaller list of targeted pools. The valid + * flags are divided into groups, where each group contains bits that + * describe mutually exclusive attributes of a pool, and where all bits + * within a group describe all possible pools. + * + * The first group of @flags is VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE (online) + * and VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE (offline) to filter the pools + * by state. + * + * The second group of @flags is VIR_CONNECT_LIST_STORAGE_POOLS_PERSITENT + * (defined) and VIR_CONNECT_LIST_STORAGE_POOLS_TRANSIENT (running but not + * defined), to filter the pools by whether they have persistent config or not. + * + * The third group of @flags is VIR_CONNECT_LIST_STORAGE_POOLS_AUTOSTART + * and VIR_CONNECT_LIST_STORAGE_POOLS_NO_AUTOSTART, to filter the pools by + * whether they are marked as autostart or not. + * + * The last group of @flags is provided to filter the pools by the types, + * the flags include: + * VIR_CONNECT_LIST_STORAGE_POOLS_DIR + * VIR_CONNECT_LIST_STORAGE_POOLS_FS + * VIR_CONNECT_LIST_STORAGE_POOLS_NETFS + * VIR_CONNECT_LIST_STORAGE_POOLS_LOGICAL + * VIR_CONNECT_LIST_STORAGE_POOLS_DISK + * VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI + * VIR_CONNECT_LIST_STORAGE_POOLS_SCSI + * VIR_CONNECT_LIST_STORAGE_POOLS_MPATH + * VIR_CONNECT_LIST_STORAGE_POOLS_RBD + * VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG + * + * Returns the number of storage pools found or -1 and sets @pools to + * NULL in case of error. On success, the array stored into @pools 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 virStoragePoolFree() on each array element, then calling + * free() on @pools. + */ +int +virConnectListAllStoragePools(virConnectPtr conn, + virStoragePoolPtr **pools, + unsigned int flags) +{ + VIR_DEBUG("conn=%p, pools=%p, flags=%x", conn, pools, flags); + + virResetLastError(); + + if (pools) + *pools = NULL; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->storageDriver && + conn->storageDriver->listAllPools) { + int ret; + ret = conn->storageDriver->listAllPools(conn, pools, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + +/** * virConnectNumOfStoragePools: * @conn: pointer to hypervisor connection * @@ -11259,11 +11343,17 @@ error: * @names: array of char * to fill with pool names (allocated by caller) * @maxnames: size of the names array * - * Provides the list of names of active storage pools - * upto maxnames. If there are more than maxnames, the - * remaining names will be silently ignored. + * Provides the list of names of active storage pools up to maxnames. + * If there are more than maxnames, the remaining names will be silently + * ignored. * - * Returns 0 on success, -1 on error + * For more control over the results, see virConnectListAllStoragePools(). + * + * Returns the number of pools found or -1 in case of error. Note that + * this command is inherently racy; a pool can be started between a call to + * virConnectNumOfStoragePools() and this call; you are only guaranteed + * that all currently active pools were listed if the return is less than + * @maxnames. */ int virConnectListStoragePools(virConnectPtr conn, @@ -11342,11 +11432,17 @@ error: * @names: array of char * to fill with pool names (allocated by caller) * @maxnames: size of the names array * - * Provides the list of names of inactive storage pools - * upto maxnames. If there are more than maxnames, the - * remaining names will be silently ignored. + * Provides the list of names of inactive storage pools up to maxnames. + * If there are more than maxnames, the remaining names will be silently + * ignored. * - * Returns 0 on success, -1 on error + * For more control over the results, see virConnectListAllStoragePools(). + * + * Returns the number of names provided in the array or -1 in case of error. + * Note that this command is inherently racy; a pool can be defined between + * a call to virConnectNumOfDefinedStoragePools() and this call; you are only + * guaranteed that all currently defined pools were listed if the return + * is less than @maxnames. The client must call free() on each returned name. */ int virConnectListDefinedStoragePools(virConnectPtr conn, diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index e3ba119..e9c9a31 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -549,6 +549,7 @@ LIBVIRT_0.10.0 { virDomainGetHostname; virConnectRegisterCloseCallback; virConnectUnregisterCloseCallback; + virConnectListAllStoragePools; } LIBVIRT_0.9.13; # .... define new API here using predicted next version number .... -- 1.7.7.3

On 08/17/2012 09:38 AM, Osier Yang wrote:
This introduces a new API to list the storage pool objects, 4 groups of flags are provided to filter the returned pools:
* Active or not
* Autostarting or not
* Persistent or not
* And the pool type.
include/libvirt/libvirt.h.in: New enum virConnectListAllStoragePoolFlags; Declare the API. python/generator.py: Skip the generating src/driver.h: (virDrvConnectListAllStoragePools) src/libvirt.c: Implementation for the API. src/libvirt_public.syms: Export the symbol. --- include/libvirt/libvirt.h.in | 33 ++++++++++++ python/generator.py | 5 +- src/driver.h | 5 ++ src/libvirt.c | 112 +++++++++++++++++++++++++++++++++++++++--- src/libvirt_public.syms | 1 + 5 files changed, 146 insertions(+), 10 deletions(-)
+++ b/src/libvirt_public.syms @@ -549,6 +549,7 @@ LIBVIRT_0.10.0 { virDomainGetHostname; virConnectRegisterCloseCallback; virConnectUnregisterCloseCallback; + virConnectListAllStoragePools; } LIBVIRT_0.9.13;
I tend to sort these blocks, but it was already unsorted before you got here :) ACK. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

src/conf/storage_conf.c: Add virStoragePoolMatch to filter the pools; Add virStoragePoolList to iterate over the pool objects with filter. src/conf/storage_conf.h: Declare virStoragePoolMatch, virStoragePoolList, and the macros for filters. src/libvirt_private.syms: Export helper virStoragePoolList. --- src/conf/storage_conf.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++ src/conf/storage_conf.h | 35 ++++++++++++++ src/libvirt_private.syms | 1 + 3 files changed, 152 insertions(+), 0 deletions(-) diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c index 3132aae..b14564f 100644 --- a/src/conf/storage_conf.c +++ b/src/conf/storage_conf.c @@ -1883,3 +1883,119 @@ void virStoragePoolObjUnlock(virStoragePoolObjPtr obj) { virMutexUnlock(&obj->lock); } + +#define MATCH(FLAG) (flags & (FLAG)) +static bool +virStoragePoolMatch(virStoragePoolObjPtr poolobj, + unsigned int flags) +{ + /* filter by active state */ + if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE) && + !((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE) && + virStoragePoolObjIsActive(poolobj)) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE) && + !virStoragePoolObjIsActive(poolobj)))) + return false; + + /* filter by persistence */ + if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_PERSISTENT) && + !((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_PERSISTENT) && + poolobj->configFile) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_TRANSIENT) && + !poolobj->configFile))) + return false; + + /* filter by autostart option */ + if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_AUTOSTART) && + !((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_AUTOSTART) && + poolobj->autostart) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_NO_AUTOSTART) && + !poolobj->autostart))) + return false; + + /* filter by pool type */ + if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_POOL_TYPE)) { + if (!((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_DIR) && + (poolobj->def->type == VIR_STORAGE_POOL_DIR)) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FS) && + (poolobj->def->type == VIR_STORAGE_POOL_FS)) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_NETFS) && + (poolobj->def->type == VIR_STORAGE_POOL_NETFS)) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_LOGICAL) && + (poolobj->def->type == VIR_STORAGE_POOL_LOGICAL)) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_DISK) && + (poolobj->def->type == VIR_STORAGE_POOL_DISK)) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI) && + (poolobj->def->type == VIR_STORAGE_POOL_ISCSI)) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_SCSI) && + (poolobj->def->type == VIR_STORAGE_POOL_SCSI)) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_MPATH) && + (poolobj->def->type == VIR_STORAGE_POOL_MPATH)) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_RBD) && + (poolobj->def->type == VIR_STORAGE_POOL_RBD)) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG) && + (poolobj->def->type == VIR_STORAGE_POOL_SHEEPDOG)))) + return false; + } + + return true; +} +#undef MATCH + +int +virStoragePoolList(virConnectPtr conn, + virStoragePoolObjList poolobjs, + virStoragePoolPtr **pools, + unsigned int flags) +{ + virStoragePoolPtr *tmp_pools = NULL; + virStoragePoolPtr pool = NULL; + int npools = 0; + int ret = -1; + int i; + + if (pools) { + if (VIR_ALLOC_N(tmp_pools, poolobjs.count + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + } + + for (i = 0; i < poolobjs.count; i++) { + virStoragePoolObjPtr poolobj = poolobjs.objs[i]; + virStoragePoolObjLock(poolobj); + if (virStoragePoolMatch(poolobj, flags)) { + if (pools) { + if (!(pool = virGetStoragePool(conn, + poolobj->def->name, + poolobj->def->uuid))) { + virStoragePoolObjUnlock(poolobj); + goto cleanup; + } + tmp_pools[npools] = pool; + } + npools++; + } + virStoragePoolObjUnlock(poolobj); + } + + if (tmp_pools) { + /* trim the array to the final size */ + ignore_value(VIR_REALLOC_N(tmp_pools, npools + 1)); + *pools = tmp_pools; + tmp_pools = NULL; + } + + ret = npools; + +cleanup: + if (tmp_pools) { + for (i = 0; i < npools; i++) { + if (tmp_pools[i]) + virStoragePoolFree(tmp_pools[i]); + } + } + + VIR_FREE(tmp_pools); + return ret; +} diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h index 4fb99df..bfa0819 100644 --- a/src/conf/storage_conf.h +++ b/src/conf/storage_conf.h @@ -518,4 +518,39 @@ enum virStoragePartedFsType { }; VIR_ENUM_DECL(virStoragePartedFsType) +# define VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE \ + (VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE | \ + VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE) + +# define VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_PERSISTENT \ + (VIR_CONNECT_LIST_STORAGE_POOLS_PERSISTENT | \ + VIR_CONNECT_LIST_STORAGE_POOLS_TRANSIENT) + +# define VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_AUTOSTART \ + (VIR_CONNECT_LIST_STORAGE_POOLS_AUTOSTART | \ + VIR_CONNECT_LIST_STORAGE_POOLS_NO_AUTOSTART) + +# define VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_POOL_TYPE \ + (VIR_CONNECT_LIST_STORAGE_POOLS_DIR | \ + VIR_CONNECT_LIST_STORAGE_POOLS_FS | \ + VIR_CONNECT_LIST_STORAGE_POOLS_NETFS | \ + VIR_CONNECT_LIST_STORAGE_POOLS_LOGICAL | \ + VIR_CONNECT_LIST_STORAGE_POOLS_DISK | \ + VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI | \ + VIR_CONNECT_LIST_STORAGE_POOLS_SCSI | \ + VIR_CONNECT_LIST_STORAGE_POOLS_MPATH | \ + VIR_CONNECT_LIST_STORAGE_POOLS_RBD | \ + VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG) + +# define VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ALL \ + (VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE | \ + VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_PERSISTENT | \ + VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_AUTOSTART | \ + VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_POOL_TYPE) + +int virStoragePoolList(virConnectPtr conn, + virStoragePoolObjList poolobjs, + virStoragePoolPtr **pools, + unsigned int flags); + #endif /* __VIR_STORAGE_CONF_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e91540d..bee960b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1033,6 +1033,7 @@ virStoragePoolDefParseString; virStoragePoolFormatDiskTypeToString; virStoragePoolFormatFileSystemNetTypeToString; virStoragePoolFormatFileSystemTypeToString; +virStoragePoolList; virStoragePoolLoadAllConfigs; virStoragePoolObjAssignDef; virStoragePoolObjClearVols; -- 1.7.7.3

On 08/17/2012 09:38 AM, Osier Yang wrote:
src/conf/storage_conf.c: Add virStoragePoolMatch to filter the pools; Add virStoragePoolList to iterate over the pool objects with filter.
src/conf/storage_conf.h: Declare virStoragePoolMatch, virStoragePoolList, and the macros for filters.
src/libvirt_private.syms: Export helper virStoragePoolList. --- src/conf/storage_conf.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++ src/conf/storage_conf.h | 35 ++++++++++++++ src/libvirt_private.syms | 1 + 3 files changed, 152 insertions(+), 0 deletions(-)
ACK. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

The RPC generator doesn't support returning list of object, this patch do the work manually. * daemon/remote.c: Implement the server side handler remoteDispatchConnectListAllStoragePools * src/remote/remote_driver.c: Add remote driver handler remoteConnectListAllStoragePools. * src/remote/remote_protocol.x: New RPC procedure REMOTE_PROC_CONNECT_LIST_ALL_STORAGE_POOLS and structs to represent the args and ret for it. * src/remote_protocol-structs: Likewise. --- daemon/remote.c | 54 +++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 64 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 12 +++++++- src/remote_protocol-structs | 12 ++++++++ 4 files changed, 141 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 851bcc1..d1e95a9 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3945,6 +3945,60 @@ cleanup: return rv; } +static int +remoteDispatchConnectListAllStoragePools(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_connect_list_all_storage_pools_args *args, + remote_connect_list_all_storage_pools_ret *ret) +{ + virStoragePoolPtr *pools = NULL; + int npools = 0; + int i; + int rv = -1; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if ((npools = virConnectListAllStoragePools(priv->conn, + args->need_results ? &pools : NULL, + args->flags)) < 0) + goto cleanup; + + if (pools && npools) { + if (VIR_ALLOC_N(ret->pools.pools_val, npools) < 0) { + virReportOOMError(); + goto cleanup; + } + + ret->pools.pools_len = npools; + + for (i = 0; i < npools; i++) + make_nonnull_storage_pool(ret->pools.pools_val + i, pools[i]); + } else { + ret->pools.pools_len = 0; + ret->pools.pools_val = NULL; + } + + ret->ret = npools; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (pools) { + for (i = 0; i < npools; i++) + virStoragePoolFree(pools[i]); + VIR_FREE(pools); + } + return rv; +} + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index c4941c5..fbcde99 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2673,6 +2673,69 @@ done: return rv; } +static int +remoteConnectListAllStoragePools (virConnectPtr conn, + virStoragePoolPtr **pools, + unsigned int flags) +{ + int rv = -1; + int i; + virStoragePoolPtr *tmp_pools = NULL; + remote_connect_list_all_storage_pools_args args; + remote_connect_list_all_storage_pools_ret ret; + + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + args.need_results = !!pools; + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + if (call(conn, + priv, + 0, + REMOTE_PROC_CONNECT_LIST_ALL_STORAGE_POOLS, + (xdrproc_t) xdr_remote_connect_list_all_storage_pools_args, + (char *) &args, + (xdrproc_t) xdr_remote_connect_list_all_storage_pools_ret, + (char *) &ret) == -1) + goto done; + + if (pools) { + if (VIR_ALLOC_N(tmp_pools, ret.pools.pools_len + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < ret.pools.pools_len; i++) { + tmp_pools[i] = get_nonnull_storage_pool(conn, ret.pools.pools_val[i]); + if (!tmp_pools[i]) { + virReportOOMError(); + goto cleanup; + } + } + *pools = tmp_pools; + tmp_pools = NULL; + } + + rv = ret.ret; + +cleanup: + if (tmp_pools) { + for (i = 0; i < ret.pools.pools_len; i++) + if (tmp_pools[i]) + virStoragePoolFree(tmp_pools[i]); + VIR_FREE(tmp_pools); + } + + xdr_free((xdrproc_t) xdr_remote_connect_list_all_storage_pools_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + /*----------------------------------------------------------------------*/ static virDrvOpenStatus ATTRIBUTE_NONNULL (1) @@ -5424,6 +5487,7 @@ static virStorageDriver storage_driver = { .listPools = remoteListStoragePools, /* 0.4.1 */ .numOfDefinedPools = remoteNumOfDefinedStoragePools, /* 0.4.1 */ .listDefinedPools = remoteListDefinedStoragePools, /* 0.4.1 */ + .listAllPools = remoteConnectListAllStoragePools, /* 0.10.0 */ .findPoolSources = remoteFindStoragePoolSources, /* 0.4.5 */ .poolLookupByName = remoteStoragePoolLookupByName, /* 0.4.1 */ .poolLookupByUUID = remoteStoragePoolLookupByUUID, /* 0.4.1 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 200fe75..194b01e 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2527,6 +2527,15 @@ struct remote_connect_list_all_domains_ret { unsigned int ret; }; +struct remote_connect_list_all_storage_pools_args { + int need_results; + unsigned int flags; +}; + +struct remote_connect_list_all_storage_pools_ret { + remote_nonnull_storage_pool pools<>; + unsigned int ret; +}; /*----- Protocol. -----*/ @@ -2854,7 +2863,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_LIST_ALL_SNAPSHOTS = 274, /* skipgen skipgen priority:high */ REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275, /* skipgen skipgen priority:high */ REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276, /* autogen autogen */ - REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277 /* autogen autogen */ + REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277, /* autogen autogen */ + REMOTE_PROC_CONNECT_LIST_ALL_STORAGE_POOLS = 278 /* skipgen skipgen priority:high */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 8d09138..c47ba15 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1981,6 +1981,17 @@ struct remote_connect_list_all_domains_ret { } domains; u_int ret; }; +struct remote_connect_list_all_storage_pools_args { + int need_results; + u_int flags; +}; +struct remote_connect_list_all_storage_pools_ret { + struct { + u_int pools_len; + remote_nonnull_domain * pools_val; + } pools; + u_int ret; +}; enum remote_procedure { REMOTE_PROC_OPEN = 1, REMOTE_PROC_CLOSE = 2, @@ -2259,4 +2270,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275, REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276, REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277, + REMOTE_PROC_CONNECT_LIST_ALL_STORAGE_POOLS = 278, }; -- 1.7.7.3

On 08/17/2012 09:38 AM, Osier Yang wrote:
The RPC generator doesn't support returning list of object, this patch do
s/patch do/patch does/
the work manually.
* daemon/remote.c: Implement the server side handler remoteDispatchConnectListAllStoragePools
* src/remote/remote_driver.c: Add remote driver handler remoteConnectListAllStoragePools.
* src/remote/remote_protocol.x: New RPC procedure REMOTE_PROC_CONNECT_LIST_ALL_STORAGE_POOLS and structs to represent the args and ret for it.
* src/remote_protocol-structs: Likewise. --- daemon/remote.c | 54 +++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 64 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 12 +++++++- src/remote_protocol-structs | 12 ++++++++ 4 files changed, 141 insertions(+), 1 deletions(-)
ACK. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

src/storage/storage_driver.c: Implement listAllStoragePools. --- src/storage/storage_driver.c | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 3dc66db..4f99eb9 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -2285,6 +2285,23 @@ cleanup: return ret; } +static int +storageListAllPools(virConnectPtr conn, + virStoragePoolPtr **pools, + unsigned int flags) +{ + virStorageDriverStatePtr driver = conn->storagePrivateData; + int ret = -1; + + virCheckFlags(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ALL, -1); + + storageDriverLock(driver); + ret = virStoragePoolList(conn, driver->pools, pools, flags); + storageDriverUnlock(driver); + + return ret; +} + static virStorageDriver storageDriver = { .name = "storage", .open = storageOpen, /* 0.4.0 */ @@ -2293,6 +2310,7 @@ static virStorageDriver storageDriver = { .listPools = storageListPools, /* 0.4.0 */ .numOfDefinedPools = storageNumDefinedPools, /* 0.4.0 */ .listDefinedPools = storageListDefinedPools, /* 0.4.0 */ + .listAllPools = storageListAllPools, /* 0.10.0 */ .findPoolSources = storageFindPoolSources, /* 0.4.0 */ .poolLookupByName = storagePoolLookupByName, /* 0.4.0 */ .poolLookupByUUID = storagePoolLookupByUUID, /* 0.4.0 */ -- 1.7.7.3

On 08/17/2012 09:38 AM, Osier Yang wrote:
src/storage/storage_driver.c: Implement listAllStoragePools. --- src/storage/storage_driver.c | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-)
ACK. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

src/test/test_driver.c: Implement listAllStoragePools --- src/test/test_driver.c | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index a767e21..18691ad 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -3941,6 +3941,22 @@ no_memory: return -1; } +static int +testStorageListAllPools(virConnectPtr conn, + virStoragePoolPtr **pools, + unsigned int flags) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + virCheckFlags(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ALL, -1); + + testDriverLock(privconn); + ret = virStoragePoolList(conn, privconn->pools, pools, flags); + testDriverUnlock(privconn); + + return ret; +} static int testStoragePoolIsActive(virStoragePoolPtr pool) { @@ -5659,6 +5675,7 @@ static virStorageDriver testStorageDriver = { .listPools = testStorageListPools, /* 0.5.0 */ .numOfDefinedPools = testStorageNumDefinedPools, /* 0.5.0 */ .listDefinedPools = testStorageListDefinedPools, /* 0.5.0 */ + .listAllPools = testStorageListAllPools, /* 0.10.0 */ .findPoolSources = testStorageFindPoolSources, /* 0.5.0 */ .poolLookupByName = testStoragePoolLookupByName, /* 0.5.0 */ .poolLookupByUUID = testStoragePoolLookupByUUID, /* 0.5.0 */ -- 1.7.7.3

On 08/17/2012 09:38 AM, Osier Yang wrote:
src/test/test_driver.c: Implement listAllStoragePools --- src/test/test_driver.c | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-)
ACK. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

tools/virsh.c: New helper function vshStringToArray. tools/virsh-domain.c: use the helper in cmdUndefine. --- tools/virsh-domain.c | 19 ++----------------- tools/virsh.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 676c002..d746e1e 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -2393,23 +2393,8 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd) /* tokenize the string from user and save it's parts into an array */ if (volumes) { - /* count the delimiters */ - volume_tok = volumes; - nvolume_tokens = 1; /* we need at least one member */ - while (*volume_tok) { - if (*(volume_tok++) == ',') - nvolume_tokens++; - } - - volume_tokens = vshCalloc(ctl, nvolume_tokens, sizeof(char *)); - - /* tokenize the input string */ - nvolume_tokens = 0; - volume_tok = volumes; - do { - volume_tokens[nvolume_tokens] = strsep(&volume_tok, ","); - nvolume_tokens++; - } while (volume_tok); + if ((nvolume_tokens = vshStringToArray(volumes, &volume_tokens)) < 0) + goto cleanup; } if ((nvolumes = virXPathNodeSet("./devices/disk", ctxt, diff --git a/tools/virsh.c b/tools/virsh.c index 4dff02e..3df0963 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -3204,6 +3204,51 @@ vshParseArgv(vshControl *ctl, int argc, char **argv) return true; } +/* + * Convert the strings separated by ',' into array. The caller + * must free the returned array after use. + * + * Returns the length of the filled array on success, or -1 + * on error. + */ +static int +vshStringToArray(char *str, + char ***array) +{ + char *str_tok = NULL; + unsigned int nstr_tokens = 0; + char **arr = NULL; + + /* tokenize the string from user and save it's parts into an array */ + if (str) { + nstr_tokens = 1; + + /* count the delimiters */ + str_tok = str; + while (*str_tok) { + if (*str_tok == ',') + nstr_tokens++; + str_tok++; + } + + if (VIR_ALLOC_N(arr, nstr_tokens) < 0) { + virReportOOMError(); + return -1; + } + + /* tokenize the input string */ + nstr_tokens = 0; + str_tok = str; + do { + arr[nstr_tokens] = strsep(&str_tok, ","); + nstr_tokens++; + } while (str_tok); + } + + *array = arr; + return nstr_tokens; +} + #include "virsh-domain.c" #include "virsh-domain-monitor.c" #include "virsh-pool.c" -- 1.7.7.3

On 08/17/2012 09:38 AM, Osier Yang wrote:
tools/virsh.c: New helper function vshStringToArray. tools/virsh-domain.c: use the helper in cmdUndefine. --- tools/virsh-domain.c | 19 ++----------------- tools/virsh.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 17 deletions(-)
Umm...
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 676c002..d746e1e 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -2393,23 +2393,8 @@ cmdUndefine(vshControl *ctl, const vshCmd *cmd)
/* tokenize the string from user and save it's parts into an array */ if (volumes) { - /* count the delimiters */ - volume_tok = volumes; - nvolume_tokens = 1; /* we need at least one member */ - while (*volume_tok) { - if (*(volume_tok++) == ',') - nvolume_tokens++; - } - - volume_tokens = vshCalloc(ctl, nvolume_tokens, sizeof(char *)); - - /* tokenize the input string */ - nvolume_tokens = 0; - volume_tok = volumes; - do { - volume_tokens[nvolume_tokens] = strsep(&volume_tok, ","); - nvolume_tokens++; - } while (volume_tok); + if ((nvolume_tokens = vshStringToArray(volumes, &volume_tokens)) < 0)
You are attempting to call...
+++ b/tools/virsh.c @@ -3204,6 +3204,51 @@ vshParseArgv(vshControl *ctl, int argc, char **argv) return true; }
+/* + * Convert the strings separated by ',' into array. The caller + * must free the returned array after use. + * + * Returns the length of the filled array on success, or -1 + * on error. + */ +static int +vshStringToArray(char *str, + char ***array)
...a static function from another file, with no prototype. How did this compile for you? Oh, EWWWW. Did we really split virsh.c by doing #include "virsh-*.c"? Yuck. A thousand times yuck. We _really_ need to add headers (virsh.h for common utility functions available throughout the other virsh helpers, and then headers like virsh-pool.h that declares _just_ the arrays needed for virsh.c to know the command structure provided by that category). That also means fixing tools/Makefile.am to compile all of the C files. I'm okay with factoring this out into a helper function, but not until we first clean up virsh to use proper modules, rather than compiling a single .c that includes all other .c files. Since I mentioned it, I'll see if I can help. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

The storage pool's management doesn't relate with a domain, it probably was a intention, but not achieved yet. And the fact is only active pools are listed by default. --- tools/virsh.pod | 9 ++++----- 1 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/virsh.pod b/tools/virsh.pod index 35613c4..1abed7c 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -2163,11 +2163,10 @@ Returns basic information about the I<pool> object. =item B<pool-list> [I<--inactive> | I<--all>] [I<--details>] -List pool objects known to libvirt. By default, only pools in use by -active domains are listed; I<--inactive> lists just the inactive -pools, and I<--all> lists all pools. The I<--details> option instructs -virsh to additionally display pool persistence and capacity related -information where available. +List pool objects known to libvirt. By default, only active pools +are listed; I<--inactive> lists just the inactive pools, and I<--all> +lists all pools. The I<--details> option instructs virsh to additionally +display pool persistence and capacity related information where available. =item B<pool-name> I<uuid> -- 1.7.7.3

On 08/17/2012 09:38 AM, Osier Yang wrote:
The storage pool's management doesn't relate with a domain, it probably was a intention, but not achieved yet. And the fact
s/a intention/an intention/ [and yes, that's still an intention on my back burner of cool patches to write, someday...]
is only active pools are listed by default. --- tools/virsh.pod | 9 ++++----- 1 files changed, 4 insertions(+), 5 deletions(-)
ACK. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Move definition of MATCH from virsh-domain-monitor.c into virsh.c for further use. --- tools/virsh-domain-monitor.c | 2 -- tools/virsh.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/virsh-domain-monitor.c b/tools/virsh-domain-monitor.c index 1272a05..c6bea37 100644 --- a/tools/virsh-domain-monitor.c +++ b/tools/virsh-domain-monitor.c @@ -1309,7 +1309,6 @@ vshDomainListFree(vshDomainListPtr domlist) VIR_FREE(domlist); } -#define MATCH(FLAG) (flags & (FLAG)) static vshDomainListPtr vshDomainListCollect(vshControl *ctl, unsigned int flags) { @@ -1528,7 +1527,6 @@ cleanup: VIR_FREE(ids); return list; } -#undef MATCH static const vshCmdOptDef opts_list[] = { {"inactive", VSH_OT_BOOL, 0, N_("list inactive domains")}, diff --git a/tools/virsh.c b/tools/virsh.c index 3df0963..f27dd2c 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -94,6 +94,8 @@ static char *progname; /* Default escape char Ctrl-] as per telnet */ #define CTRL_CLOSE_BRACKET "^]" +#define MATCH(FLAG) (flags & (FLAG)) + /** * The log configuration */ -- 1.7.7.3

On 08/17/2012 09:38 AM, Osier Yang wrote:
Move definition of MATCH from virsh-domain-monitor.c into virsh.c for further use. --- tools/virsh-domain-monitor.c | 2 -- tools/virsh.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-)
Again, this patch needs to wait until we fix virsh.c to provide a proper header. At which point, the macro for easier reuse should probably be named VSH_MATCH to be namespace-safe. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

tools/virsh-pool.c: * vshStoragePoolSorter to sort the pool list by pool name. * struct vshStoragePoolList to present the pool list, pool info is collected by list->poolinfo if 'details' is specified by user. * vshStoragePoolListFree to free the pool list * vshStoragePoolListCollect to collect the pool list, new API virStorageListAllPools is tried first, if it's not supported, fall back to older APIs. * New options --persistent, --transient, --autostart, --no-autostart and --type for pool-list. --persistent or --transient is to filter the returned pool list by whether the pool is persistent or not. --autostart or --no-autostart is to filter the returned pool list by whether the pool is autostarting or not. --type is to filter the pools by pool types. E.g. % virsh pool-list --all --persistent --type dir,disk tools/virsh.pod: * Add documentations for the new options. --- tools/virsh-pool.c | 441 ++++++++++++++++++++++++++++++++++++++++------------ tools/virsh.pod | 24 +++- 2 files changed, 362 insertions(+), 103 deletions(-) diff --git a/tools/virsh-pool.c b/tools/virsh-pool.c index af80427..4db67f7 100644 --- a/tools/virsh-pool.c +++ b/tools/virsh-pool.c @@ -23,6 +23,8 @@ * */ +#include "conf/storage_conf.h" + /* default is lookup by Name and UUID */ #define vshCommandOptPool(_ctl, _cmd, _optname, _name) \ vshCommandOptPoolBy(_ctl, _cmd, _optname, _name, \ @@ -571,6 +573,232 @@ cmdPoolDumpXML(vshControl *ctl, const vshCmd *cmd) return ret; } +static int +vshStoragePoolSorter(const void *a, const void *b) +{ + virStoragePoolPtr *pa = (virStoragePoolPtr *) a; + virStoragePoolPtr *pb = (virStoragePoolPtr *) b; + + if (*pa && !*pb) + return -1; + + if (!*pa) + return *pb != NULL; + + return vshStrcasecmp(virStoragePoolGetName(*pa), + virStoragePoolGetName(*pb)); +} + +struct vshStoragePoolList { + virStoragePoolPtr *pools; + size_t npools; +}; +typedef struct vshStoragePoolList *vshStoragePoolListPtr; + +static void +vshStoragePoolListFree(vshStoragePoolListPtr list) +{ + int i; + + if (list && list->pools) { + for (i = 0; i < list->npools; i++) { + if (list->pools[i]) + virStoragePoolFree(list->pools[i]); + } + VIR_FREE(list->pools); + } + VIR_FREE(list); +} + +static vshStoragePoolListPtr +vshStoragePoolListCollect(vshControl *ctl, + unsigned int flags) +{ + vshStoragePoolListPtr list = vshMalloc(ctl, sizeof(*list)); + int i; + int ret; + char **names = NULL; + virStoragePoolPtr pool; + bool success = false; + size_t deleted = 0; + int persistent; + int autostart; + int nActivePools = 0; + int nInactivePools = 0; + int nAllPools = 0; + + /* try the list with flags support (0.10.0 and later) */ + if ((ret = virConnectListAllStoragePools(ctl->conn, + &list->pools, + flags)) >= 0) { + list->npools = ret; + goto finished; + } + + /* check if the command is actually supported */ + if (last_error && last_error->code == VIR_ERR_NO_SUPPORT) { + vshResetLibvirtError(); + goto fallback; + } + + if (last_error && last_error->code == VIR_ERR_INVALID_ARG) { + /* try the new API again but mask non-guaranteed flags */ + unsigned int newflags = flags & (VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE | + VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE); + vshResetLibvirtError(); + if ((ret = virConnectListAllStoragePools(ctl->conn, &list->pools, + newflags)) >= 0) { + list->npools = ret; + goto filter; + } + } + + /* there was an error during the first or second call */ + vshError(ctl, "%s", _("Failed to list pools")); + goto cleanup; + + +fallback: + /* fall back to old method (0.9.13 and older) */ + vshResetLibvirtError(); + + /* There is no way to get the pool type */ + if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_POOL_TYPE)) { + vshError(ctl, "%s", _("Filtering using --type is not supported " + "by this libvirt")); + goto cleanup; + } + + /* Get the number of active pools */ + if (!MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE) || + MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE)) { + if ((nActivePools = virConnectNumOfStoragePools(ctl->conn)) < 0) { + vshError(ctl, "%s", _("Failed to get the number of active pools ")); + goto cleanup; + } + } + + /* Get the number of inactive pools */ + if (!MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE) || + MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE)) { + if ((nInactivePools = virConnectNumOfDefinedStoragePools(ctl->conn)) < 0) { + vshError(ctl, "%s", _("Failed to get the number of inactive pools")); + goto cleanup; + } + } + + nAllPools = nActivePools + nInactivePools; + + if (nAllPools == 0) + return list; + + names = vshMalloc(ctl, sizeof(char *) * nAllPools); + + /* Retrieve a list of active storage pool names */ + if (!MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE) || + MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE)) { + if (virConnectListStoragePools(ctl->conn, + names, nActivePools) < 0) { + vshError(ctl, "%s", _("Failed to list active pools")); + goto cleanup; + } + } + + /* Add the inactive storage pools to the end of the name list */ + if (!MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_ACTIVE) || + MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE)) { + if (virConnectListDefinedStoragePools(ctl->conn, + &names[nActivePools], + nInactivePools) < 0) { + vshError(ctl, "%s", _("Failed to list inactive pools")); + goto cleanup; + } + } + + list->pools = vshMalloc(ctl, sizeof(virStoragePoolPtr) * (nAllPools)); + list->npools = 0; + + /* get active pools */ + for (i = 0; i < nActivePools; i++) { + if (!(pool = virStoragePoolLookupByName(ctl->conn, names[i]))) + continue; + list->pools[list->npools++] = pool; + } + + /* get inactive pools */ + for (i = 0; i < nInactivePools; i++) { + if (!(pool = virStoragePoolLookupByName(ctl->conn, names[i]))) + continue; + list->pools[list->npools++] = pool; + } + + /* truncate pools that weren't found */ + deleted = nAllPools - list->npools; + +filter: + /* filter list the list if the list was acquired by fallback means */ + for (i = 0; i < list->npools; i++) { + pool = list->pools[i]; + + /* persistence filter */ + if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_PERSISTENT)) { + if ((persistent = virStoragePoolIsPersistent(pool)) < 0) { + vshError(ctl, "%s", _("Failed to get pool persistence info")); + goto cleanup; + } + + if (!((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_PERSISTENT) && persistent) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_TRANSIENT) && !persistent))) + goto remove_entry; + } + + /* autostart filter */ + if (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_FILTERS_AUTOSTART)) { + if (virStoragePoolGetAutostart(pool, &autostart) < 0) { + vshError(ctl, "%s", _("Failed to get pool autostart state")); + goto cleanup; + } + + if (!((MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_AUTOSTART) && autostart) || + (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_NO_AUTOSTART) && !autostart))) + goto remove_entry; + } + + /* the pool matched all filters, it may stay */ + continue; + +remove_entry: + /* the pool has to be removed as it failed one of the filters */ + virStoragePoolFree(list->pools[i]); + list->pools[i] = NULL; + deleted++; + } + +finished: + /* sort the list */ + if (list->pools && list->npools) + qsort(list->pools, list->npools, + sizeof(*list->pools), vshStoragePoolSorter); + + /* truncate the list if filter simulation deleted entries */ + if (deleted) + VIR_SHRINK_N(list->pools, list->npools, deleted); + + success = true; + +cleanup: + for (i = 0; i < nAllPools; i++) + VIR_FREE(names[i]); + + if (!success) { + vshStoragePoolListFree(list); + list = NULL; + } + + VIR_FREE(names); + return list; +} + /* * "pool-list" command */ @@ -583,7 +811,12 @@ static const vshCmdInfo info_pool_list[] = { static const vshCmdOptDef opts_pool_list[] = { {"inactive", VSH_OT_BOOL, 0, N_("list inactive pools")}, {"all", VSH_OT_BOOL, 0, N_("list inactive & active pools")}, + {"transient", VSH_OT_BOOL, 0, N_("list transient pools")}, + {"persistent", VSH_OT_BOOL, 0, N_("list persistent pools")}, + {"autostart", VSH_OT_BOOL, 0, N_("list pools with autostart enabled")}, + {"no-autostart", VSH_OT_BOOL, 0, N_("list pools with autostart disabled")}, {"details", VSH_OT_BOOL, 0, N_("display extended details for pools")}, + {"type", VSH_OT_STRING, 0, N_("only list pool of specified type(s) (if supported)")}, {NULL, 0, 0, NULL} }; @@ -591,10 +824,8 @@ static bool cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { virStoragePoolInfo info; - char **poolNames = NULL; int i, ret; - bool functionReturn; - int numActivePools = 0, numInactivePools = 0, numAllPools = 0; + bool functionReturn = false; size_t stringLength = 0, nameStrLength = 0; size_t autostartStrLength = 0, persistStrLength = 0; size_t stateStrLength = 0, capStrLength = 0; @@ -608,84 +839,103 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) char *available; }; struct poolInfoText *poolInfoTexts = NULL; - - /* Determine the options passed by the user */ - bool all = vshCommandOptBool(cmd, "all"); + unsigned int flags = VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE; + vshStoragePoolListPtr list = NULL; + const char *type = NULL; bool details = vshCommandOptBool(cmd, "details"); - bool inactive = vshCommandOptBool(cmd, "inactive"); - bool active = !inactive || all; - inactive |= all; + bool inactive, all; - /* Check the connection to libvirtd daemon is still working */ - if (!vshConnectionUsability(ctl, ctl->conn)) - return false; + inactive = vshCommandOptBool(cmd, "inactive"); + all = vshCommandOptBool(cmd, "all"); - /* Retrieve the number of active storage pools */ - if (active) { - numActivePools = virConnectNumOfStoragePools(ctl->conn); - if (numActivePools < 0) { - vshError(ctl, "%s", _("Failed to list active pools")); - return false; - } - } + if (inactive) + flags = VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE; - /* Retrieve the number of inactive storage pools */ - if (inactive) { - numInactivePools = virConnectNumOfDefinedStoragePools(ctl->conn); - if (numInactivePools < 0) { - vshError(ctl, "%s", _("Failed to list inactive pools")); - return false; - } - } + if (all) + flags = VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE | + VIR_CONNECT_LIST_STORAGE_POOLS_INACTIVE; - /* Determine the total number of pools to list */ - numAllPools = numActivePools + numInactivePools; + if (vshCommandOptBool(cmd, "autostart")) + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_AUTOSTART; - /* Allocate memory for arrays of storage pool names and info */ - poolNames = vshCalloc(ctl, numAllPools, sizeof(*poolNames)); - poolInfoTexts = - vshCalloc(ctl, numAllPools, sizeof(*poolInfoTexts)); + if (vshCommandOptBool(cmd, "no-autostart")) + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_NO_AUTOSTART; - /* Retrieve a list of active storage pool names */ - if (active) { - if (virConnectListStoragePools(ctl->conn, - poolNames, numActivePools) < 0) { - vshError(ctl, "%s", _("Failed to list active pools")); - VIR_FREE(poolInfoTexts); - VIR_FREE(poolNames); - return false; - } + if (vshCommandOptBool(cmd, "persistent")) + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_PERSISTENT; + + if (vshCommandOptBool(cmd, "transient")) + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_TRANSIENT; + + if (vshCommandOptString(cmd, "type", &type) < 0) { + vshError(ctl, "%s", _("Invalid argument for 'type'")); + return false; } - /* Add the inactive storage pools to the end of the name list */ - if (inactive) { - if (virConnectListDefinedStoragePools(ctl->conn, - &poolNames[numActivePools], - numInactivePools) < 0) { - vshError(ctl, "%s", _("Failed to list inactive pools")); - VIR_FREE(poolInfoTexts); - VIR_FREE(poolNames); - return false; + if (type) { + int poolType = -1; + char **poolTypes = NULL; + int npoolTypes = 0; + + npoolTypes = vshStringToArray((char *)type, &poolTypes); + + for (i = 0; i < npoolTypes; i++) { + if ((poolType = virStoragePoolTypeFromString(poolTypes[i])) < 0) { + vshError(ctl, "%s", _("Invalid pool type")); + VIR_FREE(poolTypes); + return false; + } + + switch(poolType) { + case VIR_STORAGE_POOL_DIR: + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_DIR; + break; + case VIR_STORAGE_POOL_FS: + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_FS; + break; + case VIR_STORAGE_POOL_NETFS: + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_NETFS; + break; + case VIR_STORAGE_POOL_LOGICAL: + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_LOGICAL; + break; + case VIR_STORAGE_POOL_DISK: + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_DISK; + break; + case VIR_STORAGE_POOL_ISCSI: + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI; + break; + case VIR_STORAGE_POOL_SCSI: + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_SCSI; + break; + case VIR_STORAGE_POOL_MPATH: + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_MPATH; + break; + case VIR_STORAGE_POOL_RBD: + flags |= VIR_CONNECT_LIST_STORAGE_POOLS_RBD; + break; + default: + break; + } } + VIR_FREE(poolTypes); } - /* Sort the storage pool names */ - qsort(poolNames, numAllPools, sizeof(*poolNames), vshNameSorter); + /* Check the connection to libvirtd daemon is still working */ + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(list = vshStoragePoolListCollect(ctl, flags))) + goto cleanup; + + poolInfoTexts = vshCalloc(ctl, list->npools, sizeof(*poolInfoTexts)); /* Collect the storage pool information for display */ - for (i = 0; i < numAllPools; i++) { + for (i = 0; i < list->npools; i++) { int autostart = 0, persistent = 0; - /* Retrieve a pool object, looking it up by name */ - virStoragePoolPtr pool = virStoragePoolLookupByName(ctl->conn, - poolNames[i]); - if (!pool) { - VIR_FREE(poolNames[i]); - continue; - } - /* Retrieve the autostart status of the pool */ - if (virStoragePoolGetAutostart(pool, &autostart) < 0) + if (virStoragePoolGetAutostart(list->pools[i], &autostart) < 0) poolInfoTexts[i].autostart = vshStrdup(ctl, _("no autostart")); else poolInfoTexts[i].autostart = vshStrdup(ctl, autostart ? @@ -693,7 +943,7 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) /* Retrieve the persistence status of the pool */ if (details) { - persistent = virStoragePoolIsPersistent(pool); + persistent = virStoragePoolIsPersistent(list->pools[i]); vshDebug(ctl, VSH_ERR_DEBUG, "Persistent flag value: %d\n", persistent); if (persistent < 0) @@ -709,7 +959,7 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) } /* Collect further extended information about the pool */ - if (virStoragePoolGetInfo(pool, &info) != 0) { + if (virStoragePoolGetInfo(list->pools[i], &info) != 0) { /* Something went wrong retrieving pool info, cope with it */ vshError(ctl, "%s", _("Could not retrieve pool information")); poolInfoTexts[i].state = vshStrdup(ctl, _("unknown")); @@ -751,28 +1001,25 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) val = prettyCapacity(info.capacity, &unit); ret = virAsprintf(&poolInfoTexts[i].capacity, "%.2lf %s", val, unit); - if (ret < 0) { + if (ret < 0) /* An error occurred creating the string, return */ goto asprintf_failure; - } /* Create the allocation output string */ val = prettyCapacity(info.allocation, &unit); ret = virAsprintf(&poolInfoTexts[i].allocation, "%.2lf %s", val, unit); - if (ret < 0) { + if (ret < 0) /* An error occurred creating the string, return */ goto asprintf_failure; - } /* Create the available space output string */ val = prettyCapacity(info.available, &unit); ret = virAsprintf(&poolInfoTexts[i].available, "%.2lf %s", val, unit); - if (ret < 0) { + if (ret < 0) /* An error occurred creating the string, return */ goto asprintf_failure; - } } else { /* Capacity related information isn't available */ poolInfoTexts[i].capacity = vshStrdup(ctl, _("-")); @@ -796,16 +1043,16 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) availStrLength = stringLength; } else { /* --details option was not specified, only active/inactive - * state strings are used */ - if (info.state == VIR_STORAGE_POOL_INACTIVE) - poolInfoTexts[i].state = vshStrdup(ctl, _("inactive")); - else + * state strings are used */ + if (virStoragePoolIsActive(list->pools[i])) poolInfoTexts[i].state = vshStrdup(ctl, _("active")); - } + else + poolInfoTexts[i].state = vshStrdup(ctl, _("inactive")); + } } /* Keep the length of name string if longest so far */ - stringLength = strlen(poolNames[i]); + stringLength = strlen(virStoragePoolGetName(list->pools[i])); if (stringLength > nameStrLength) nameStrLength = stringLength; @@ -818,9 +1065,6 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) stringLength = strlen(poolInfoTexts[i].autostart); if (stringLength > autostartStrLength) autostartStrLength = stringLength; - - /* Free the pool object */ - virStoragePoolFree(pool); } /* If the --details option wasn't selected, we output the pool @@ -836,9 +1080,10 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) vshPrintExtra(ctl, "-----------------------------------------\n"); /* Output old style pool info */ - for (i = 0; i < numAllPools; i++) { + for (i = 0; i < list->npools; i++) { + const char *name = virStoragePoolGetName(list->pools[i]); vshPrint(ctl, "%-20s %-10s %-10s\n", - poolNames[i], + name, poolInfoTexts[i].state, poolInfoTexts[i].autostart); } @@ -930,9 +1175,9 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) vshPrintExtra(ctl, "\n"); /* Display the pool info rows */ - for (i = 0; i < numAllPools; i++) { + for (i = 0; i < list->npools; i++) { vshPrint(ctl, outputStr, - poolNames[i], + virStoragePoolGetName(list->pools[i]), poolInfoTexts[i].state, poolInfoTexts[i].autostart, poolInfoTexts[i].persistent, @@ -946,7 +1191,6 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) goto cleanup; asprintf_failure: - /* Display an appropriate error message then cleanup and return */ switch (errno) { case ENOMEM: @@ -960,24 +1204,19 @@ asprintf_failure: functionReturn = false; cleanup: - - /* Safely free the memory allocated in this function */ - for (i = 0; i < numAllPools; i++) { - /* Cleanup the memory for one pool info structure */ - VIR_FREE(poolInfoTexts[i].state); - VIR_FREE(poolInfoTexts[i].autostart); - VIR_FREE(poolInfoTexts[i].persistent); - VIR_FREE(poolInfoTexts[i].capacity); - VIR_FREE(poolInfoTexts[i].allocation); - VIR_FREE(poolInfoTexts[i].available); - VIR_FREE(poolNames[i]); + if (list && list->npools) { + for (i = 0; i < list->npools; i++) { + VIR_FREE(poolInfoTexts[i].state); + VIR_FREE(poolInfoTexts[i].autostart); + VIR_FREE(poolInfoTexts[i].persistent); + VIR_FREE(poolInfoTexts[i].capacity); + VIR_FREE(poolInfoTexts[i].allocation); + VIR_FREE(poolInfoTexts[i].available); + } } - - /* Cleanup the memory for the initial arrays*/ VIR_FREE(poolInfoTexts); - VIR_FREE(poolNames); - /* Return the desired value */ + vshStoragePoolListFree(list); return functionReturn; } diff --git a/tools/virsh.pod b/tools/virsh.pod index 1abed7c..7517a00 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -2161,13 +2161,33 @@ variables, and defaults to C<vi>. Returns basic information about the I<pool> object. -=item B<pool-list> [I<--inactive> | I<--all>] [I<--details>] +=item B<pool-list> [I<--inactive>] [I<--all>] + [I<--persistent>] [I<--transient>] + [I<--autostart>] [I<--no-autostart>] + [[I<--details>] [<type>] List pool objects known to libvirt. By default, only active pools are listed; I<--inactive> lists just the inactive pools, and I<--all> -lists all pools. The I<--details> option instructs virsh to additionally +lists all pools. + +Except the default, I<--inactive>, and I<--all>, you may want to specify more +filtering flags. I<--persistent> is to list the persistent pools, I<--transient> +is to list the transient pools. I<--autostart> is to list the autostarting pools, +I<--no-autostart> is to list the pools with autostarting disabled. + +You may also want to list pools with specified types using I<type>, the +pool types must be separated by comma, e.g. --type dir,disk. The valid pool +types include 'dir', 'fs', 'netfs', 'logical', 'disk', 'iscsi', 'scsi', +'mpath', 'rbd', and 'sheepdog'. + +The I<--details> option instructs virsh to additionally display pool persistence and capacity related information where available. +NOTE: When talking to older servers, this command is forced to use a series of +API calls with an inherent race, where a pool might not be listed or might appear +more than once if it changed state between calls while the list was being +collected. Newer servers do not have this problem. + =item B<pool-name> I<uuid> Convert the I<uuid> to a pool name. -- 1.7.7.3

The implementation is done manually as the generator does not support wrapping lists of C pointers into Python objects. python/libvirt-override-api.xml: Document python/libvirt-override-virConnect.py: Add listAllStoragePools python/libvirt-override.c: Implementation for the wrapper. --- python/libvirt-override-api.xml | 6 ++++ python/libvirt-override-virConnect.py | 12 ++++++++ python/libvirt-override.c | 47 +++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 0 deletions(-) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 67ef36e..d16755c 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -306,6 +306,12 @@ <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> <return type='str *' info='the list of Names of None in case of error'/> </function> + <function name='virConnectListAllStoragePools' file='python'> + <info>returns list of all storage pools</info> + <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> + <arg name='flags' type='unsigned int' info='optional flags'/> + <return type='pool *' info='the list of pools or None in case of error'/> + </function> <function name='virStoragePoolListVolumes' file='python'> <info>list the storage volumes, stores the pointers to the names in @names</info> <arg name='pool' type='virStoragePoolPtr' info='pointer to the storage pool'/> diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index 50177ab..87a737f 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -206,3 +206,15 @@ retlist.append(virDomain(self, _obj=domptr)) return retlist + + def listAllStoragePools(self, flags): + """Returns a list of storage pool objects""" + ret = libvirtmod.virConnectListAllStoragePools(self._o, flags) + if ret is None: + raise libvirtError("virConnectListAllStoragePools() failed", conn=self) + + retlist = list() + for poolptr in ret: + retlist.append(virStoragePool(self, _obj=poolptr)) + + return retlist diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 8b41dff..0b1d509 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -2983,6 +2983,52 @@ libvirt_virConnectListDefinedStoragePools(PyObject *self ATTRIBUTE_UNUSED, return py_retval; } +static PyObject * +libvirt_virConnectListAllStoragePools(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *pyobj_conn; + PyObject *py_retval = NULL; + PyObject *tmp = NULL; + virConnectPtr conn; + virStoragePoolPtr *pools = NULL; + int c_retval = 0; + int i; + unsigned int flags; + + if (!PyArg_ParseTuple(args, (char *)"Oi:virConnectListAllStoragePools", + &pyobj_conn, &flags)) + return NULL; + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virConnectListAllStoragePools(conn, &pools, flags); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + + if (!(py_retval = PyList_New(c_retval))) + goto cleanup; + + for (i = 0; i < c_retval; i++) { + if (!(tmp = libvirt_virStoragePoolPtrWrap(pools[i])) || + PyList_SetItem(py_retval, i, tmp) < 0) { + Py_XDECREF(tmp); + Py_DECREF(py_retval); + py_retval = NULL; + goto cleanup; + } + /* python steals the pointer */ + pools[i] = NULL; + } + +cleanup: + for (i = 0; i < c_retval; i++) + if (pools[i]) + virStoragePoolFree(pools[i]); + VIR_FREE(pools); + return py_retval; +} static PyObject * libvirt_virStoragePoolListVolumes(PyObject *self ATTRIBUTE_UNUSED, @@ -5874,6 +5920,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainGetVcpuPinInfo", libvirt_virDomainGetVcpuPinInfo, METH_VARARGS, NULL}, {(char *) "virConnectListStoragePools", libvirt_virConnectListStoragePools, METH_VARARGS, NULL}, {(char *) "virConnectListDefinedStoragePools", libvirt_virConnectListDefinedStoragePools, METH_VARARGS, NULL}, + {(char *) "virConnectListAllStoragePools", libvirt_virConnectListAllStoragePools, METH_VARARGS, NULL}, {(char *) "virStoragePoolGetAutostart", libvirt_virStoragePoolGetAutostart, METH_VARARGS, NULL}, {(char *) "virStoragePoolListVolumes", libvirt_virStoragePoolListVolumes, METH_VARARGS, NULL}, {(char *) "virStoragePoolGetInfo", libvirt_virStoragePoolGetInfo, METH_VARARGS, NULL}, -- 1.7.7.3

Simply returns the storage volume objects. No supported filter flags. include/libvirt/libvirt.h.in: Declare the API python/generator.py: Skip the function for generating. virStoragePool.py will be added in later patch. src/driver.h: virDrvStoragePoolListVolumesFlags src/libvirt.c: Implementation for the API. src/libvirt_public.syms: Export the symbol to public --- include/libvirt/libvirt.h.in | 3 ++ python/generator.py | 1 + src/driver.h | 6 ++++- src/libvirt.c | 50 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 5 files changed, 60 insertions(+), 1 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 1e17207..953237b 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2624,6 +2624,9 @@ int virStoragePoolNumOfVolumes (virStoragePoolPtr pool) int virStoragePoolListVolumes (virStoragePoolPtr pool, char **const names, int maxnames); +int virStoragePoolListAllVolumes (virStoragePoolPtr pool, + virStorageVolPtr **vols, + unsigned int flags); virConnectPtr virStorageVolGetConnect (virStorageVolPtr vol); diff --git a/python/generator.py b/python/generator.py index 6fab68c..554ce26 100755 --- a/python/generator.py +++ b/python/generator.py @@ -459,6 +459,7 @@ skip_function = ( 'virDomainListAllSnapshots', # overridden in virDomain.py 'virDomainSnapshotListAllChildren', # overridden in virDomainSnapshot.py 'virConnectListAllStoragePools', # overridden in virConnect.py + 'virStoragePoolListAllVolumes', # overridden in virStoragePool.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 3c1ae3b..b4273c1 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1291,7 +1291,10 @@ typedef int (*virDrvStoragePoolListVolumes) (virStoragePoolPtr pool, char **const names, int maxnames); - +typedef int + (*virDrvStoragePoolListAllVolumes) (virStoragePoolPtr pool, + virStorageVolPtr **vols, + unsigned int flags); typedef virStorageVolPtr (*virDrvStorageVolLookupByName) (virStoragePoolPtr pool, @@ -1399,6 +1402,7 @@ struct _virStorageDriver { virDrvStoragePoolSetAutostart poolSetAutostart; virDrvStoragePoolNumOfVolumes poolNumOfVolumes; virDrvStoragePoolListVolumes poolListVolumes; + virDrvStoragePoolListAllVolumes poolListAllVolumes; virDrvStorageVolLookupByName volLookupByName; virDrvStorageVolLookupByKey volLookupByKey; diff --git a/src/libvirt.c b/src/libvirt.c index 82f5956..c4b2b73 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -12398,6 +12398,54 @@ error: return -1; } +/** + * virStoragePoolListAllVolumes: + * @pool: Pointer to storage pool + * @vols: Pointer to a variable to store the array containing storage volume + * objects or NULL if the list is not required (just returns number + * of volumes). + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Collect the list of storage volumes, and allocate an array to store those + * objects. + * + * Returns the number of storage volumes found or -1 and sets @vols to + * NULL in case of error. On success, the array stored into @vols 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 virStorageVolFree() on each array element, then calling + * free() on @vols. + */ +int +virStoragePoolListAllVolumes(virStoragePoolPtr pool, + virStorageVolPtr **vols, + unsigned int flags) +{ + VIR_DEBUG("pool=%p, vols=%p, flags=%x", pool, vols, flags); + + virResetLastError(); + + if (!VIR_IS_STORAGE_POOL(pool)) { + virLibConnError(VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (pool->conn->storageDriver && + pool->conn->storageDriver->poolListAllVolumes) { + int ret; + ret = pool->conn->storageDriver->poolListAllVolumes(pool, vols, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(pool->conn); + return -1; +} /** * virStoragePoolNumOfVolumes: @@ -12445,6 +12493,8 @@ error: * Fetch list of storage volume names, limiting to * at most maxnames. * + * To list the volume objects directly, see virStoragePoolListAllVolumes(). + * * Returns the number of names fetched, or -1 on error */ int diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index e9c9a31..fa6137a 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -550,6 +550,7 @@ LIBVIRT_0.10.0 { virConnectRegisterCloseCallback; virConnectUnregisterCloseCallback; virConnectListAllStoragePools; + virStoragePoolListAllVolumes; } LIBVIRT_0.9.13; # .... define new API here using predicted next version number .... -- 1.7.7.3

The RPC generator doesn't returning support list of object, this patch do the work manually. * daemon/remote.c: Implemente the server side handler remoteDispatchStoragePoolListAllVolumes * src/remote/remote_driver.c: Add remote driver handler remoteStoragePoolListAllVolumes * src/remote/remote_protocol.x: New RPC procedure REMOTE_PROC_STORAGE_POOL_LIST_ALL_VOLUMES and structs to represent the args and ret for it. * src/remote_protocol-structs: Likewise. --- daemon/remote.c | 58 ++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 66 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 14 ++++++++- src/remote_protocol-structs | 13 ++++++++ 4 files changed, 150 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index d1e95a9..80158b6 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -3999,6 +3999,64 @@ cleanup: return rv; } +static int +remoteDispatchStoragePoolListAllVolumes(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_storage_pool_list_all_volumes_args *args, + remote_storage_pool_list_all_volumes_ret *ret) +{ + virStorageVolPtr *vols = NULL; + virStoragePoolPtr pool = NULL; + int nvols = 0; + int i; + int rv = -1; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if (!(pool = get_nonnull_storage_pool(priv->conn, args->pool))) + goto cleanup; + + if ((nvols = virStoragePoolListAllVolumes(pool, + args->need_results ? &vols : NULL, + args->flags)) < 0) + goto cleanup; + + if (vols && nvols) { + if (VIR_ALLOC_N(ret->vols.vols_val, nvols) < 0) { + virReportOOMError(); + goto cleanup; + } + + ret->vols.vols_len = nvols; + + for (i = 0; i < nvols; i++) + make_nonnull_storage_vol(ret->vols.vols_val + i, vols[i]); + } else { + ret->vols.vols_len = 0; + ret->vols.vols_val = NULL; + } + + ret->ret = nvols; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (vols) { + for (i = 0; i < nvols; i++) + virStorageVolFree(vols[i]); + VIR_FREE(vols); + } + return rv; +} + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index fbcde99..098bf2c 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2736,6 +2736,71 @@ done: return rv; } +static int +remoteStoragePoolListAllVolumes(virStoragePoolPtr pool, + virStorageVolPtr **vols, + unsigned int flags) +{ + int rv = -1; + int i; + virStorageVolPtr *tmp_vols = NULL; + remote_storage_pool_list_all_volumes_args args; + remote_storage_pool_list_all_volumes_ret ret; + + struct private_data *priv = pool->conn->privateData; + + remoteDriverLock(priv); + + make_nonnull_storage_pool(&args.pool, pool); + args.need_results = !!vols; + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + if (call(pool->conn, + priv, + 0, + REMOTE_PROC_STORAGE_POOL_LIST_ALL_VOLUMES, + (xdrproc_t) xdr_remote_storage_pool_list_all_volumes_args, + (char *) &args, + (xdrproc_t) xdr_remote_storage_pool_list_all_volumes_ret, + (char *) &ret) == -1) + goto done; + + if (vols) { + if (VIR_ALLOC_N(tmp_vols, ret.vols.vols_len + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < ret.vols.vols_len; i++) { + tmp_vols[i] = get_nonnull_storage_vol(pool->conn, ret.vols.vols_val[i]); + if (!tmp_vols[i]) { + virReportOOMError(); + goto cleanup; + } + } + *vols = tmp_vols; + tmp_vols = NULL; + } + + rv = ret.ret; + +cleanup: + if (tmp_vols) { + for (i = 0; i < ret.vols.vols_len; i++) + if (tmp_vols[i]) + virStorageVolFree(tmp_vols[i]); + VIR_FREE(tmp_vols); + } + + xdr_free((xdrproc_t) xdr_remote_storage_pool_list_all_volumes_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + + /*----------------------------------------------------------------------*/ static virDrvOpenStatus ATTRIBUTE_NONNULL (1) @@ -5506,6 +5571,7 @@ static virStorageDriver storage_driver = { .poolSetAutostart = remoteStoragePoolSetAutostart, /* 0.4.1 */ .poolNumOfVolumes = remoteStoragePoolNumOfVolumes, /* 0.4.1 */ .poolListVolumes = remoteStoragePoolListVolumes, /* 0.4.1 */ + .poolListAllVolumes = remoteStoragePoolListAllVolumes, /* 0.10.0 */ .volLookupByName = remoteStorageVolLookupByName, /* 0.4.1 */ .volLookupByKey = remoteStorageVolLookupByKey, /* 0.4.1 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 194b01e..45a49fb 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2537,6 +2537,17 @@ struct remote_connect_list_all_storage_pools_ret { unsigned int ret; }; +struct remote_storage_pool_list_all_volumes_args { + remote_nonnull_storage_pool pool; + int need_results; + unsigned int flags; +}; + +struct remote_storage_pool_list_all_volumes_ret { + remote_nonnull_storage_vol vols<>; + unsigned int ret; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -2864,7 +2875,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275, /* skipgen skipgen priority:high */ REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276, /* autogen autogen */ REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277, /* autogen autogen */ - REMOTE_PROC_CONNECT_LIST_ALL_STORAGE_POOLS = 278 /* skipgen skipgen priority:high */ + REMOTE_PROC_CONNECT_LIST_ALL_STORAGE_POOLS = 278, /* skipgen skipgen priority:high */ + REMOTE_PROC_STORAGE_POOL_LIST_ALL_VOLUMES = 279 /* skipgen skipgen priority:high */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index c47ba15..d425a8b 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1992,6 +1992,18 @@ struct remote_connect_list_all_storage_pools_ret { } pools; u_int ret; }; +struct remote_storage_pool_list_all_volumes_args { + remote_nonnull_storage_pool pool; + int need_results; + u_int flags; +}; +struct remote_storage_pool_list_all_volumes_ret { + struct { + u_int vols_len; + remote_nonnull_storage_vol * vols_val; + } vols; + u_int ret; +}; enum remote_procedure { REMOTE_PROC_OPEN = 1, REMOTE_PROC_CLOSE = 2, @@ -2271,4 +2283,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276, REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277, REMOTE_PROC_CONNECT_LIST_ALL_STORAGE_POOLS = 278, + REMOTE_PROC_STORAGE_POOL_LIST_ALL_VOLUMES = 279, }; -- 1.7.7.3

src/storage/storage_driver.c: Implement poolListAllVolumes. --- src/storage/storage_driver.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 67 insertions(+), 0 deletions(-) diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 4f99eb9..4f83348 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -1153,6 +1153,72 @@ storagePoolListVolumes(virStoragePoolPtr obj, return -1; } +static int +storagePoolListAllVolumes(virStoragePoolPtr pool, + virStorageVolPtr **vols, + unsigned int flags) { + virStorageDriverStatePtr driver = pool->conn->storagePrivateData; + virStoragePoolObjPtr obj; + int i; + virStorageVolPtr *tmp_vols = NULL; + virStorageVolPtr vol = NULL; + int nvols = 0; + int ret = -1; + + virCheckFlags(0, -1); + + storageDriverLock(driver); + obj = virStoragePoolObjFindByUUID(&driver->pools, pool->uuid); + storageDriverUnlock(driver); + + if (!obj) { + virReportError(VIR_ERR_NO_STORAGE_POOL, "%s", + _("no storage pool with matching uuid")); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(obj)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("storage pool is not active")); + goto cleanup; + } + + /* Just returns the volumes count */ + if (!vols) { + ret = obj->volumes.count; + goto cleanup; + } + + if (VIR_ALLOC_N(tmp_vols, obj->volumes.count + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0 ; i < obj->volumes.count; i++) { + if (!(vol = virGetStorageVol(pool->conn, obj->def->name, + obj->volumes.objs[i]->name, + obj->volumes.objs[i]->key))) + goto cleanup; + tmp_vols[nvols++] = vol; + } + + *vols = tmp_vols; + tmp_vols = NULL; + ret = nvols; + + cleanup: + if (tmp_vols) { + for (i = 0; i < nvols; i++) { + if (tmp_vols[i]) + virStorageVolFree(tmp_vols[i]); + } + } + + if (obj) + virStoragePoolObjUnlock(obj); + + return ret; +} static virStorageVolPtr storageVolumeLookupByName(virStoragePoolPtr obj, @@ -2329,6 +2395,7 @@ static virStorageDriver storageDriver = { .poolSetAutostart = storagePoolSetAutostart, /* 0.4.0 */ .poolNumOfVolumes = storagePoolNumVolumes, /* 0.4.0 */ .poolListVolumes = storagePoolListVolumes, /* 0.4.0 */ + .poolListAllVolumes = storagePoolListAllVolumes, /* 0.10.0 */ .volLookupByName = storageVolumeLookupByName, /* 0.4.0 */ .volLookupByKey = storageVolumeLookupByKey, /* 0.4.0 */ -- 1.7.7.3

src/test/test_driver.c: Implement poolListAllVolumes. --- src/test/test_driver.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 67 insertions(+), 0 deletions(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 18691ad..f7913aa 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -4552,6 +4552,72 @@ testStoragePoolListVolumes(virStoragePoolPtr pool, return -1; } +static int +testStoragePoolListAllVolumes(virStoragePoolPtr obj, + virStorageVolPtr **vols, + unsigned int flags) { + testConnPtr privconn = obj->conn->privateData; + virStoragePoolObjPtr pool; + int i; + virStorageVolPtr *tmp_vols = NULL; + virStorageVolPtr vol = NULL; + int nvols = 0; + int ret = -1; + + virCheckFlags(0, -1); + + testDriverLock(privconn); + pool = virStoragePoolObjFindByUUID(&privconn->pools, obj->uuid); + testDriverUnlock(privconn); + + if (!pool) { + virReportError(VIR_ERR_NO_STORAGE_POOL, "%s", + _("no storage pool with matching uuid")); + goto cleanup; + } + + if (!virStoragePoolObjIsActive(pool)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("storage pool is not active")); + goto cleanup; + } + + /* Just returns the volumes count */ + if (!vols) { + ret = pool->volumes.count; + goto cleanup; + } + + if (VIR_ALLOC_N(tmp_vols, pool->volumes.count + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0 ; i < pool->volumes.count; i++) { + if (!(vol = virGetStorageVol(obj->conn, pool->def->name, + pool->volumes.objs[i]->name, + pool->volumes.objs[i]->key))) + goto cleanup; + tmp_vols[nvols++] = vol; + } + + *vols = tmp_vols; + tmp_vols = NULL; + ret = nvols; + + cleanup: + if (tmp_vols) { + for (i = 0; i < nvols; i++) { + if (tmp_vols[i]) + virStorageVolFree(tmp_vols[i]); + } + } + + if (pool) + virStoragePoolObjUnlock(pool); + + return ret; +} static virStorageVolPtr testStorageVolumeLookupByName(virStoragePoolPtr pool, @@ -5694,6 +5760,7 @@ static virStorageDriver testStorageDriver = { .poolSetAutostart = testStoragePoolSetAutostart, /* 0.5.0 */ .poolNumOfVolumes = testStoragePoolNumVolumes, /* 0.5.0 */ .poolListVolumes = testStoragePoolListVolumes, /* 0.5.0 */ + .poolListAllVolumes = testStoragePoolListAllVolumes, /* 0.10.0 */ .volLookupByName = testStorageVolumeLookupByName, /* 0.5.0 */ .volLookupByKey = testStorageVolumeLookupByKey, /* 0.5.0 */ -- 1.7.7.3

tools/virsh-volume.c: * vshStorageVolSorter to sort storage vols by name * vshStorageVolumeListFree to free the volume objects list * vshStorageVolumeListCollect to collect the volume objects, trying to use new API first, fall back to older APIs if it's not supported. --- tools/virsh-volume.c | 197 ++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 150 insertions(+), 47 deletions(-) diff --git a/tools/virsh-volume.c b/tools/virsh-volume.c index 5e5d925..2460f26 100644 --- a/tools/virsh-volume.c +++ b/tools/virsh-volume.c @@ -985,6 +985,133 @@ cmdVolDumpXML(vshControl *ctl, const vshCmd *cmd) return ret; } +static int +vshStorageVolSorter(const void *a, const void *b) +{ + virStorageVolPtr *va = (virStorageVolPtr *) a; + virStorageVolPtr *vb = (virStorageVolPtr *) b; + + if (*va && !*vb) + return -1; + + if (!*va) + return *vb != NULL; + + return vshStrcasecmp(virStorageVolGetName(*va), + virStorageVolGetName(*vb)); +} + +struct vshStorageVolList { + virStorageVolPtr *vols; + size_t nvols; +}; +typedef struct vshStorageVolList *vshStorageVolListPtr; + +static void +vshStorageVolListFree(vshStorageVolListPtr list) +{ + int i; + + if (list && list->vols) { + for (i = 0; i < list->nvols; i++) { + if (list->vols[i]) + virStorageVolFree(list->vols[i]); + } + VIR_FREE(list->vols); + } + VIR_FREE(list); +} + +static vshStorageVolListPtr +vshStorageVolListCollect(vshControl *ctl, + virStoragePoolPtr pool, + unsigned int flags) +{ + vshStorageVolListPtr list = vshMalloc(ctl, sizeof(*list)); + int i; + char **names = NULL; + virStorageVolPtr vol = NULL; + bool success = false; + size_t deleted = 0; + int nvols = 0; + int ret = -1; + + /* try the list with flags support (0.10.0 and later) */ + if ((ret = virStoragePoolListAllVolumes(pool, + &list->vols, + flags)) >= 0) { + list->nvols = ret; + goto finished; + } + + /* check if the command is actually supported */ + if (last_error && last_error->code == VIR_ERR_NO_SUPPORT) { + vshResetLibvirtError(); + goto fallback; + } + + /* there was an error during the call */ + vshError(ctl, "%s", _("Failed to list volumes")); + goto cleanup; + +fallback: + /* fall back to old method (0.9.13 and older) */ + vshResetLibvirtError(); + + /* Determine the number of volumes in the pool */ + if ((nvols = virStoragePoolNumOfVolumes(pool)) < 0) { + vshError(ctl, "%s", _("Failed to list storage volumes")); + goto cleanup; + } + + if (nvols == 0) { + success = true; + return list; + } + + /* Retrieve the list of volume names in the pool */ + names = vshCalloc(ctl, nvols, sizeof(*names)); + if ((nvols = virStoragePoolListVolumes(pool, names, nvols)) < 0) { + vshError(ctl, "%s", _("Failed to list storage volumes")); + goto cleanup; + } + + list->vols = vshMalloc(ctl, sizeof(virStorageVolPtr) * (nvols)); + list->nvols = 0; + + /* get the vols */ + for (i = 0; i < nvols; i++) { + if (!(vol = virStorageVolLookupByName(pool, names[i]))) + continue; + list->vols[list->nvols++] = vol; + } + + /* truncate the list for not found vols */ + deleted = nvols - list->nvols; + +finished: + /* sort the list */ + if (list->vols && list->nvols) + qsort(list->vols, list->nvols, sizeof(*list->vols), vshStorageVolSorter); + + if (deleted) + VIR_SHRINK_N(list->vols, list->nvols, deleted); + + success = true; + +cleanup: + for (i = 0; i < nvols; i++) + VIR_FREE(names[i]); + VIR_FREE(names); + + if (!success) { + vshStorageVolListFree(list); + list = NULL; + } + + return list; +} + /* * "vol-list" command */ @@ -1005,14 +1132,13 @@ cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { virStorageVolInfo volumeInfo; virStoragePoolPtr pool; - char **activeNames = NULL; char *outputStr = NULL; const char *unit; double val; bool details = vshCommandOptBool(cmd, "details"); - int numVolumes = 0, i; + int i; int ret; - bool functionReturn; + bool functionReturn = false; int stringLength = 0; size_t allocStrLength = 0, capStrLength = 0; size_t nameStrLength = 0, pathStrLength = 0; @@ -1024,6 +1150,7 @@ cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) char *type; }; struct volInfoText *volInfoTexts = NULL; + vshStorageVolListPtr list = NULL; /* Check the connection to libvirtd daemon is still working */ if (!vshConnectionUsability(ctl, ctl->conn)) @@ -1033,38 +1160,16 @@ cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) if (!(pool = vshCommandOptPool(ctl, cmd, "pool", NULL))) return false; - /* Determine the number of volumes in the pool */ - numVolumes = virStoragePoolNumOfVolumes(pool); - - if (numVolumes < 0) { - vshError(ctl, "%s", _("Failed to list storage volumes")); - virStoragePoolFree(pool); - return false; - } - - /* Retrieve the list of volume names in the pool */ - if (numVolumes > 0) { - activeNames = vshCalloc(ctl, numVolumes, sizeof(*activeNames)); - if ((numVolumes = virStoragePoolListVolumes(pool, activeNames, - numVolumes)) < 0) { - vshError(ctl, "%s", _("Failed to list active vols")); - VIR_FREE(activeNames); - virStoragePoolFree(pool); - return false; - } - - /* Sort the volume names */ - qsort(&activeNames[0], numVolumes, sizeof(*activeNames), vshNameSorter); + if (!(list = vshStorageVolListCollect(ctl, pool, 0))) + goto cleanup; - /* Set aside memory for volume information pointers */ - volInfoTexts = vshCalloc(ctl, numVolumes, sizeof(*volInfoTexts)); - } + if (list->nvols > 0) + volInfoTexts = vshCalloc(ctl, list->nvols, sizeof(*volInfoTexts)); /* Collect the rest of the volume information for display */ - for (i = 0; i < numVolumes; i++) { + for (i = 0; i < list->nvols; i++) { /* Retrieve volume info */ - virStorageVolPtr vol = virStorageVolLookupByName(pool, - activeNames[i]); + virStorageVolPtr vol = list->vols[i]; /* Retrieve the volume path */ if ((volInfoTexts[i].path = virStorageVolGetPath(vol)) == NULL) { @@ -1122,7 +1227,7 @@ cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) */ /* Keep the length of name string if longest so far */ - stringLength = strlen(activeNames[i]); + stringLength = strlen(virStorageVolGetName(list->vols[i])); if (stringLength > nameStrLength) nameStrLength = stringLength; @@ -1146,9 +1251,6 @@ cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) if (stringLength > allocStrLength) allocStrLength = stringLength; } - - /* Cleanup memory allocation */ - virStorageVolFree(vol); } /* If the --details option wasn't selected, we output the volume @@ -1161,8 +1263,8 @@ cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) /* The old output format */ vshPrintExtra(ctl, "%-20s %-40s\n", _("Name"), _("Path")); vshPrintExtra(ctl, "-----------------------------------------\n"); - for (i = 0; i < numVolumes; i++) { - vshPrint(ctl, "%-20s %-40s\n", activeNames[i], + for (i = 0; i < list->nvols; i++) { + vshPrint(ctl, "%-20s %-40s\n", virStorageVolGetName(list->vols[i]), volInfoTexts[i].path); } @@ -1233,9 +1335,9 @@ cmdVolList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) vshPrintExtra(ctl, "\n"); /* Display the volume info rows */ - for (i = 0; i < numVolumes; i++) { + for (i = 0; i < list->nvols; i++) { vshPrint(ctl, outputStr, - activeNames[i], + virStorageVolGetName(list->vols[i]), volInfoTexts[i].path, volInfoTexts[i].type, volInfoTexts[i].capacity, @@ -1263,20 +1365,21 @@ asprintf_failure: cleanup: /* Safely free the memory allocated in this function */ - for (i = 0; i < numVolumes; i++) { - /* Cleanup the memory for one volume info structure per loop */ - VIR_FREE(volInfoTexts[i].path); - VIR_FREE(volInfoTexts[i].type); - VIR_FREE(volInfoTexts[i].capacity); - VIR_FREE(volInfoTexts[i].allocation); - VIR_FREE(activeNames[i]); + if (list && list->nvols) { + for (i = 0; i < list->nvols; i++) { + /* Cleanup the memory for one volume info structure per loop */ + VIR_FREE(volInfoTexts[i].path); + VIR_FREE(volInfoTexts[i].type); + VIR_FREE(volInfoTexts[i].capacity); + VIR_FREE(volInfoTexts[i].allocation); + } } /* Cleanup remaining memory */ VIR_FREE(outputStr); VIR_FREE(volInfoTexts); - VIR_FREE(activeNames); virStoragePoolFree(pool); + vshStorageVolListFree(list); /* Return the desired value */ return functionReturn; -- 1.7.7.3

The implementation is done manually as the generator does not support wrapping lists of C pointers into Python objects. python/libvirt-override-api.xml: Document python/libvirt-override-virStoragePool.py: * New file, includes implementation of listAllVolumes. python/libvirt-override.c: Implementation for the wrapper. --- python/libvirt-override-api.xml | 8 ++++- python/libvirt-override-virStoragePool.py | 11 ++++++ python/libvirt-override.c | 50 +++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletions(-) create mode 100644 python/libvirt-override-virStoragePool.py diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index d16755c..8a228fb 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -315,7 +315,13 @@ <function name='virStoragePoolListVolumes' file='python'> <info>list the storage volumes, stores the pointers to the names in @names</info> <arg name='pool' type='virStoragePoolPtr' info='pointer to the storage pool'/> - <return type='str *' info='the list of Names of None in case of error'/> + <return type='str *' info='the list of Names or None in case of error'/> + </function> + <function name='virStoragePoolListAllVolumes' file='python'> + <info>return list of storage volume objects</info> + <arg name='pool' type='virStoragePoolPtr' info='pointer to the storage pool'/> + <arg name='flags' type='unsigned int' info='optional flags'/> + <return type='volume *' info='the list of volumes or None in case of error'/> </function> <function name='virStoragePoolGetInfo' file='python'> <info>Extract information about a storage pool. Note that if the connection used to get the domain is limited only a partial set of the information can be extracted.</info> diff --git a/python/libvirt-override-virStoragePool.py b/python/libvirt-override-virStoragePool.py new file mode 100644 index 0000000..ffe160c --- /dev/null +++ b/python/libvirt-override-virStoragePool.py @@ -0,0 +1,11 @@ + def listAllVolumes(self, flags): + """List all storage volumes and returns a list of storage volume objects""" + ret = libvirtmod.virStoragePoolListAllVolumes(self._o, flags) + if ret is None: + raise libvirtError("virStoragePoolListAllVolumes() failed", conn=self) + + retlist = list() + for volptr in ret: + retlist.append(virStorageVol(self, _obj=volptr)) + + return retlist diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 0b1d509..322a8d2 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3083,6 +3083,55 @@ libvirt_virStoragePoolListVolumes(PyObject *self ATTRIBUTE_UNUSED, } static PyObject * +libvirt_virStoragePoolListAllVolumes(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *py_retval = NULL; + PyObject *tmp = NULL; + virStoragePoolPtr pool; + virStorageVolPtr *vols = NULL; + int c_retval = 0; + int i; + unsigned int flags; + PyObject *pyobj_pool; + + if (!PyArg_ParseTuple(args, (char *)"Oi:virStoragePoolListAllVolumes", + &pyobj_pool, &flags)) + return NULL; + + pool = (virStoragePoolPtr) PyvirStoragePool_Get(pyobj_pool); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virStoragePoolListAllVolumes(pool, &vols, flags); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + + if (!(py_retval = PyList_New(c_retval))) + goto cleanup; + + for (i = 0; i < c_retval; i++) { + if (!(tmp = libvirt_virStorageVolPtrWrap(vols[i])) || + PyList_SetItem(py_retval, i, tmp) < 0) { + Py_XDECREF(tmp); + Py_DECREF(py_retval); + py_retval = NULL; + goto cleanup; + } + /* python steals the pointer */ + vols[i] = NULL; + } + +cleanup: + for (i = 0; i < c_retval; i++) + if (vols[i]) + virStorageVolFree(vols[i]); + VIR_FREE(vols); + return py_retval; +} + + +static PyObject * libvirt_virStoragePoolGetAutostart(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { PyObject *py_retval; int c_retval, autostart; @@ -5923,6 +5972,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virConnectListAllStoragePools", libvirt_virConnectListAllStoragePools, METH_VARARGS, NULL}, {(char *) "virStoragePoolGetAutostart", libvirt_virStoragePoolGetAutostart, METH_VARARGS, NULL}, {(char *) "virStoragePoolListVolumes", libvirt_virStoragePoolListVolumes, METH_VARARGS, NULL}, + {(char *) "virStoragePoolListAllVolumes", libvirt_virStoragePoolListAllVolumes, METH_VARARGS, NULL}, {(char *) "virStoragePoolGetInfo", libvirt_virStoragePoolGetInfo, METH_VARARGS, NULL}, {(char *) "virStorageVolGetInfo", libvirt_virStorageVolGetInfo, METH_VARARGS, NULL}, {(char *) "virStoragePoolGetUUID", libvirt_virStoragePoolGetUUID, METH_VARARGS, NULL}, -- 1.7.7.3

This is to list the network objects, supported filtering flags are: active|inactive, persistent|transient, autostart|no-autostart. include/libvirt/libvirt.h.in: Declare enum virConnectListAllNetworkFlags and virConnectListAllNetworks. python/generator.py: Skip auto-generating src/driver.h: (virDrvConnectListAllNetworks) src/libvirt.c: Implement the public API src/libvirt_public.syms: Export the symbol to public --- include/libvirt/libvirt.h.in | 20 ++++++++++ python/generator.py | 1 + src/driver.h | 5 ++ src/libvirt.c | 86 +++++++++++++++++++++++++++++++++++++++++- src/libvirt_public.syms | 1 + 5 files changed, 111 insertions(+), 2 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 953237b..49b2ab7 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2249,6 +2249,26 @@ int virConnectNumOfDefinedNetworks (virConnectPtr conn); int virConnectListDefinedNetworks (virConnectPtr conn, char **const names, int maxnames); +/* + * virConnectListAllNetworks: + * + * Flags used to filter the returned networks. Flags in each group + * are exclusive attributes of a network. + */ +typedef enum { + VIR_CONNECT_LIST_NETWORKS_INACTIVE = 1 << 0, + VIR_CONNECT_LIST_NETWORKS_ACTIVE = 1 << 1, + + VIR_CONNECT_LIST_NETWORKS_PERSISTENT = 1 << 2, + VIR_CONNECT_LIST_NETWORKS_TRANSIENT = 1 << 3, + + VIR_CONNECT_LIST_NETWORKS_AUTOSTART = 1 << 4, + VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART = 1 << 5, +} virConnectListAllNetworksFlags; + +int virConnectListAllNetworks (virConnectPtr conn, + virNetworkPtr **nets, + unsigned int flags); /* * Lookup network by name or uuid diff --git a/python/generator.py b/python/generator.py index 554ce26..6380497 100755 --- a/python/generator.py +++ b/python/generator.py @@ -460,6 +460,7 @@ skip_function = ( 'virDomainSnapshotListAllChildren', # overridden in virDomainSnapshot.py 'virConnectListAllStoragePools', # overridden in virConnect.py 'virStoragePoolListAllVolumes', # overridden in virStoragePool.py + 'virConnectListAllNetworks', # overridden in virConnect.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 b4273c1..147a92f 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1062,6 +1062,10 @@ typedef int (*virDrvListDefinedNetworks) (virConnectPtr conn, char **const names, int maxnames); +typedef int + (*virDrvListAllNetworks) (virConnectPtr conn, + virNetworkPtr **nets, + unsigned int flags); typedef virNetworkPtr (*virDrvNetworkLookupByUUID) (virConnectPtr conn, const unsigned char *uuid); @@ -1120,6 +1124,7 @@ struct _virNetworkDriver { virDrvListNetworks listNetworks; virDrvNumOfDefinedNetworks numOfDefinedNetworks; virDrvListDefinedNetworks listDefinedNetworks; + virDrvListAllNetworks listAllNetworks; virDrvNetworkLookupByUUID networkLookupByUUID; virDrvNetworkLookupByName networkLookupByName; virDrvNetworkCreateXML networkCreateXML; diff --git a/src/libvirt.c b/src/libvirt.c index c4b2b73..2bbcf9d 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -9601,6 +9601,76 @@ virNetworkGetConnect (virNetworkPtr net) } /** + * virConnectListAllNetworks: + * @conn: Pointer to the hypervisor connection. + * @nets: Pointer to a variable to store the array containing the network + * objects or NULL if the list is not required (just returns number + * of networks). + * @flags: bitwise-OR of virConnectListAllNetworksFlags. + * + * Collect the list of networks, and allocate an array to store those + * objects. This API solves the race inherent between virConnectListNetworks + * and virConnectListDefinedNetworks. + * + * Normally, all networks are returned; however, @flags can be used to + * filter the results for a smaller list of targeted networks. The valid + * flags are divided into groups, where each group contains bits that + * describe mutually exclusive attributes of a network, and where all bits + * within a group describe all possible networks. + * + * The first group of @flags is VIR_CONNECT_LIST_NETWORKS_ACTIVE (up) and + * VIR_CONNECT_LIST_NETWORKS_INACTIVE (down) to filter the networks by state. + * + * The second group of @flags is VIR_CONNECT_LIST_NETWORKS_PERSISTENT (defined) + * and VIR_CONNECT_LIST_NETWORKS_TRANSIENT (running but not defined), to filter + * the networks by whether they have persistent config or not. + * + * The third group of @flags is VIR_CONNECT_LIST_NETWORKS_AUTOSTART + * and VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART, to filter the networks by + * whether they are marked as autostart or not. + * + * Returns the number of networks found or -1 and sets @nets to NULL in case + * of error. On success, the array stored into @nets 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 + * virNetworkFree() on each array element, then calling free() on @nets. + */ +int +virConnectListAllNetworks(virConnectPtr conn, + virNetworkPtr **nets, + unsigned int flags) +{ + VIR_DEBUG("conn=%p, nets=%p, flags=%x", conn, nets, flags); + + virResetLastError(); + + if (nets) + *nets = NULL; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->networkDriver && + conn->networkDriver->listAllNetworks) { + int ret; + ret = conn->networkDriver->listAllNetworks(conn, nets, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + + +/** * virConnectNumOfNetworks: * @conn: pointer to the hypervisor connection * @@ -9644,7 +9714,13 @@ error: * * Collect the list of active networks, and store their names in @names * - * Returns the number of networks found or -1 in case of error + * For more control over the results, see virConnectListAllNetworks(). + * + * Returns the number of networks found or -1 in case of error. Note that + * this command is inherently racy; a network can be started between a call + * to virConnectNumOfNetworks() and this call; you are only guaranteed that + * all currently active networks were listed if the return is less than + * @maxnames. */ int virConnectListNetworks(virConnectPtr conn, char **const names, int maxnames) @@ -9721,7 +9797,13 @@ error: * * list the inactive networks, stores the pointers to the names in @names * - * Returns the number of names provided in the array or -1 in case of error + * For more control over the results, see virConnectListAllNetworks(). + * + * Returns the number of names provided in the array or -1 in case of error. + * Note that this command is inherently racy; a network can be defined between + * a call to virConnectNumOfDefinedNetworks() and this call; you are only + * guaranteed that all currently defined networks were listed if the return + * is less than @maxnames. The client must call free() on each returned name. */ int virConnectListDefinedNetworks(virConnectPtr conn, char **const names, diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index fa6137a..94ec37f 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -551,6 +551,7 @@ LIBVIRT_0.10.0 { virConnectUnregisterCloseCallback; virConnectListAllStoragePools; virStoragePoolListAllVolumes; + virConnectListAllNetworks; } LIBVIRT_0.9.13; # .... define new API here using predicted next version number .... -- 1.7.7.3

The RPC generator doesn't support returning list of object, this patch do the work manually. * daemon/remote.c: Implemente the server side handler remoteDispatchConnectListAllNetworks. * src/remote/remote_driver.c: Add remote driver handler remoteConnectListAllNetworks. * src/remote/remote_protocol.x: New RPC procedure REMOTE_PROC_CONNECT_LIST_ALL_NETWORKS and structs to represent the args and ret for it. * src/remote_protocol-structs: Likewise. --- daemon/remote.c | 55 ++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 64 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 13 ++++++++- src/remote_protocol-structs | 12 ++++++++ 4 files changed, 143 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 80158b6..f0313e7 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4057,6 +4057,61 @@ cleanup: return rv; } +static int +remoteDispatchConnectListAllNetworks(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_connect_list_all_networks_args *args, + remote_connect_list_all_networks_ret *ret) +{ + virNetworkPtr *nets = NULL; + int nnets = 0; + int i; + int rv = -1; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if ((nnets = virConnectListAllNetworks(priv->conn, + args->need_results ? &nets : NULL, + args->flags)) < 0) + goto cleanup; + + if (nets && nnets) { + if (VIR_ALLOC_N(ret->nets.nets_val, nnets) < 0) { + virReportOOMError(); + goto cleanup; + } + + ret->nets.nets_len = nnets; + + for (i = 0; i < nnets; i++) + make_nonnull_network(ret->nets.nets_val + i, nets[i]); + } else { + ret->nets.nets_len = 0; + ret->nets.nets_val = NULL; + } + + ret->ret = nnets; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (nets) { + for (i = 0; i < nnets; i++) + virNetworkFree(nets[i]); + VIR_FREE(nets); + } + return rv; +} + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 098bf2c..0729a73 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2800,6 +2800,69 @@ done: return rv; } +static int +remoteConnectListAllNetworks(virConnectPtr conn, + virNetworkPtr **nets, + unsigned int flags) +{ + int rv = -1; + int i; + virNetworkPtr *tmp_nets = NULL; + remote_connect_list_all_networks_args args; + remote_connect_list_all_networks_ret ret; + + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + args.need_results = !!nets; + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + if (call(conn, + priv, + 0, + REMOTE_PROC_CONNECT_LIST_ALL_NETWORKS, + (xdrproc_t) xdr_remote_connect_list_all_networks_args, + (char *) &args, + (xdrproc_t) xdr_remote_connect_list_all_networks_ret, + (char *) &ret) == -1) + goto done; + + if (nets) { + if (VIR_ALLOC_N(tmp_nets, ret.nets.nets_len + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < ret.nets.nets_len; i++) { + tmp_nets[i] = get_nonnull_network (conn, ret.nets.nets_val[i]); + if (!tmp_nets[i]) { + virReportOOMError(); + goto cleanup; + } + } + *nets = tmp_nets; + tmp_nets = NULL; + } + + rv = ret.ret; + +cleanup: + if (tmp_nets) { + for (i = 0; i < ret.nets.nets_len; i++) + if (tmp_nets[i]) + virNetworkFree(tmp_nets[i]); + VIR_FREE(tmp_nets); + } + + xdr_free((xdrproc_t) xdr_remote_connect_list_all_networks_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + /*----------------------------------------------------------------------*/ @@ -5508,6 +5571,7 @@ static virNetworkDriver network_driver = { .listNetworks = remoteListNetworks, /* 0.3.0 */ .numOfDefinedNetworks = remoteNumOfDefinedNetworks, /* 0.3.0 */ .listDefinedNetworks = remoteListDefinedNetworks, /* 0.3.0 */ + .listAllNetworks = remoteConnectListAllNetworks, /* 0.10.0 */ .networkLookupByUUID = remoteNetworkLookupByUUID, /* 0.3.0 */ .networkLookupByName = remoteNetworkLookupByName, /* 0.3.0 */ .networkCreateXML = remoteNetworkCreateXML, /* 0.3.0 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 45a49fb..d095a2c 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2548,6 +2548,16 @@ struct remote_storage_pool_list_all_volumes_ret { unsigned int ret; }; +struct remote_connect_list_all_networks_args { + int need_results; + unsigned int flags; +}; + +struct remote_connect_list_all_networks_ret { + remote_nonnull_network nets<>; + unsigned int ret; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -2876,7 +2886,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276, /* autogen autogen */ REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277, /* autogen autogen */ REMOTE_PROC_CONNECT_LIST_ALL_STORAGE_POOLS = 278, /* skipgen skipgen priority:high */ - REMOTE_PROC_STORAGE_POOL_LIST_ALL_VOLUMES = 279 /* skipgen skipgen priority:high */ + REMOTE_PROC_STORAGE_POOL_LIST_ALL_VOLUMES = 279, /* skipgen skipgen priority:high */ + REMOTE_PROC_CONNECT_LIST_ALL_NETWORKS = 280 /* skipgen skipgen priority:high */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index d425a8b..fe3c627 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2004,6 +2004,17 @@ struct remote_storage_pool_list_all_volumes_ret { } vols; u_int ret; }; +struct remote_list_all_networks_args { + int need_results; + u_int flags; +}; +struct remote_list_all_networks_ret { + struct { + u_int nets_len; + remote_nonnull_network * nets_val; + } nets; + u_int ret; +}; enum remote_procedure { REMOTE_PROC_OPEN = 1, REMOTE_PROC_CLOSE = 2, @@ -2284,4 +2295,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277, REMOTE_PROC_CONNECT_LIST_ALL_STORAGE_POOLS = 278, REMOTE_PROC_STORAGE_POOL_LIST_ALL_VOLUMES = 279, + REMOTE_PROC_CONNECT_LIST_ALL_NETWORKS = 280, }; -- 1.7.7.3

src/conf/network_conf.c: Add virNetworkMatch to filter the networks; and virNetworkList to iterate over all the networks with the filter. src/conf/network_conf.h: Declare virNetworkList and define the macros for filters. src/libvirt_private.syms: Export virNetworkList. --- src/conf/network_conf.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++ src/conf/network_conf.h | 22 +++++++++++ src/libvirt_private.syms | 1 + 3 files changed, 114 insertions(+), 0 deletions(-) diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index db8c62f..e243154 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -1953,3 +1953,94 @@ void virNetworkObjUnlock(virNetworkObjPtr obj) { virMutexUnlock(&obj->lock); } + +#define MATCH(FLAG) (flags & (FLAG)) +static bool +virNetworkMatch (virNetworkObjPtr netobj, + unsigned int flags) +{ + /* filter by active state */ + if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) && + !((MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE) && + virNetworkObjIsActive(netobj)) || + (MATCH(VIR_CONNECT_LIST_NETWORKS_INACTIVE) && + !virNetworkObjIsActive(netobj)))) + return false; + + /* filter by persistence */ + if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT) && + !((MATCH(VIR_CONNECT_LIST_NETWORKS_PERSISTENT) && + netobj->persistent) || + (MATCH(VIR_CONNECT_LIST_NETWORKS_TRANSIENT) && + !netobj->persistent))) + return false; + + /* filter by autostart option */ + if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART) && + !((MATCH(VIR_CONNECT_LIST_NETWORKS_AUTOSTART) && + netobj->autostart) || + (MATCH(VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART) && + !netobj->autostart))) + return false; + + return true; +} +#undef MATCH + +int +virNetworkList(virConnectPtr conn, + virNetworkObjList netobjs, + virNetworkPtr **nets, + unsigned int flags) +{ + virNetworkPtr *tmp_nets = NULL; + virNetworkPtr net = NULL; + int nnets = 0; + int ret = -1; + int i; + + if (nets) { + if (VIR_ALLOC_N(tmp_nets, netobjs.count + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + } + + for (i = 0; i < netobjs.count; i++) { + virNetworkObjPtr netobj = netobjs.objs[i]; + virNetworkObjLock(netobj); + if (virNetworkMatch(netobj, flags)) { + if (nets) { + if (!(net = virGetNetwork(conn, + netobj->def->name, + netobj->def->uuid))) { + virNetworkObjUnlock(netobj); + goto cleanup; + } + tmp_nets[nnets] = net; + } + nnets++; + } + virNetworkObjUnlock(netobj); + } + + if (tmp_nets) { + /* trim the array to the final size */ + ignore_value(VIR_REALLOC_N(tmp_nets, nnets + 1)); + *nets = tmp_nets; + tmp_nets = NULL; + } + + ret = nnets; + +cleanup: + if (tmp_nets) { + for (i = 0; i < nnets; i++) { + if (tmp_nets[i]) + virNetworkFree(tmp_nets[i]); + } + } + + VIR_FREE(tmp_nets); + return ret; +} diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index a029f70..583e38f 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -302,4 +302,26 @@ void virNetworkObjUnlock(virNetworkObjPtr obj); VIR_ENUM_DECL(virNetworkForward) +# define VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE \ + (VIR_CONNECT_LIST_NETWORKS_ACTIVE | \ + VIR_CONNECT_LIST_NETWORKS_INACTIVE) + +# define VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT \ + (VIR_CONNECT_LIST_NETWORKS_PERSISTENT | \ + VIR_CONNECT_LIST_NETWORKS_TRANSIENT) + +# define VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART \ + (VIR_CONNECT_LIST_NETWORKS_AUTOSTART | \ + VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART) + +# define VIR_CONNECT_LIST_NETWORKS_FILTERS_ALL \ + (VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE | \ + VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT | \ + VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART) + +int virNetworkList(virConnectPtr conn, + virNetworkObjList netobjs, + virNetworkPtr **nets, + unsigned int flags); + #endif /* __NETWORK_CONF_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bee960b..ec337f9 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -814,6 +814,7 @@ virNetworkFindByName; virNetworkFindByUUID; virNetworkIpDefNetmask; virNetworkIpDefPrefix; +virNetworkList; virNetworkLoadAllConfigs; virNetworkObjIsDuplicate; virNetworkObjListFree; -- 1.7.7.3

src/network/bridge_driver.c: Implement listAllNetworks. --- src/network/bridge_driver.c | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 474bbfa..d968f5f 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -2202,6 +2202,22 @@ static int networkListDefinedNetworks(virConnectPtr conn, char **const names, in return -1; } +static int +networkListAllNetworks(virConnectPtr conn, + virNetworkPtr **nets, + unsigned int flags) +{ + struct network_driver *driver = conn->networkPrivateData; + int ret = -1; + + virCheckFlags(VIR_CONNECT_LIST_NETWORKS_FILTERS_ALL, -1); + + networkDriverLock(driver); + ret = virNetworkList(conn, driver->networks, nets, flags); + networkDriverUnlock(driver); + + return ret; +} static int networkIsActive(virNetworkPtr net) { @@ -2732,6 +2748,7 @@ static virNetworkDriver networkDriver = { .listNetworks = networkListNetworks, /* 0.2.0 */ .numOfDefinedNetworks = networkNumDefinedNetworks, /* 0.2.0 */ .listDefinedNetworks = networkListDefinedNetworks, /* 0.2.0 */ + .listAllNetworks = networkListAllNetworks, /* 0.10.0 */ .networkLookupByUUID = networkLookupByUUID, /* 0.2.0 */ .networkLookupByName = networkLookupByName, /* 0.2.0 */ .networkCreateXML = networkCreate, /* 0.2.0 */ -- 1.7.7.3

src/test/test_driver.c: Implement listAllNetworks. --- src/test/test_driver.c | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-) diff --git a/src/test/test_driver.c b/src/test/test_driver.c index f7913aa..7b4dd02 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -3036,6 +3036,22 @@ no_memory: return -1; } +static int +testNetworkListAllNetworks(virConnectPtr conn, + virNetworkPtr **nets, + unsigned int flags) +{ + testConnPtr privconn = conn->privateData; + int ret = -1; + + virCheckFlags(VIR_CONNECT_LIST_NETWORKS_FILTERS_ALL, -1); + + testDriverLock(privconn); + ret = virNetworkList(conn, privconn->networks, nets, flags); + testDriverUnlock(privconn); + + return ret; +} static int testNetworkIsActive(virNetworkPtr net) { @@ -5695,6 +5711,7 @@ static virNetworkDriver testNetworkDriver = { .listNetworks = testListNetworks, /* 0.3.2 */ .numOfDefinedNetworks = testNumDefinedNetworks, /* 0.3.2 */ .listDefinedNetworks = testListDefinedNetworks, /* 0.3.2 */ + .listAllNetworks = testNetworkListAllNetworks, /* 0.10.0 */ .networkLookupByUUID = testLookupNetworkByUUID, /* 0.3.2 */ .networkLookupByName = testLookupNetworkByName, /* 0.3.2 */ .networkCreateXML = testNetworkCreate, /* 0.3.2 */ -- 1.7.7.3

tools/virsh-network.c: * vshNetworkSorter to sort networks by name * vshNetworkListFree to free the network objects list. * vshNetworkListCollect to collect the network objects, trying to use new API first, fall back to older APIs if it's not supported. * New options --persistent, --transient, --autostart, --no-autostart, for net-list, and new field 'Persistent' for its output. tools/virsh.pod: * Add documents for the new options. --- tools/virsh-network.c | 351 +++++++++++++++++++++++++++++++++++++------------ tools/virsh.pod | 12 ++- 2 files changed, 275 insertions(+), 88 deletions(-) diff --git a/tools/virsh-network.c b/tools/virsh-network.c index 49ec34f..dfd3e5b 100644 --- a/tools/virsh-network.c +++ b/tools/virsh-network.c @@ -23,6 +23,8 @@ * */ +#include "conf/network_conf.h" + /* default is lookup by Name and UUID */ #define vshCommandOptNetwork(_ctl, _cmd, _name) \ vshCommandOptNetworkBy(_ctl, _cmd, _name, \ @@ -349,6 +351,225 @@ cmdNetworkInfo(vshControl *ctl, const vshCmd *cmd) return true; } +static int +vshNetworkSorter(const void *a, const void *b) +{ + virNetworkPtr *na = (virNetworkPtr *) a; + virNetworkPtr *nb = (virNetworkPtr *) b; + + if (*na && !*nb) + return -1; + + if (!*na) + return *nb != NULL; + + return vshStrcasecmp(virNetworkGetName(*na), + virNetworkGetName(*nb)); +} + +struct vshNetworkList { + virNetworkPtr *nets; + size_t nnets; +}; +typedef struct vshNetworkList *vshNetworkListPtr; + +static void +vshNetworkListFree(vshNetworkListPtr list) +{ + int i; + + if (list && list->nnets) { + for (i = 0; i < list->nnets; i++) { + if (list->nets[i]) + virNetworkFree(list->nets[i]); + } + VIR_FREE(list->nets); + } + VIR_FREE(list); +} + +static vshNetworkListPtr +vshNetworkListCollect(vshControl *ctl, + unsigned int flags) +{ + vshNetworkListPtr list = vshMalloc(ctl, sizeof(*list)); + int i; + int ret; + char **names = NULL; + virNetworkPtr net; + bool success = false; + size_t deleted = 0; + int persistent; + int autostart; + int nActiveNets = 0; + int nInactiveNets = 0; + int nAllNets = 0; + + /* try the list with flags support (0.10.0 and later) */ + if ((ret = virConnectListAllNetworks(ctl->conn, + &list->nets, + flags)) >= 0) { + list->nnets = ret; + goto finished; + } + + /* check if the command is actually supported */ + if (last_error && last_error->code == VIR_ERR_NO_SUPPORT) { + vshResetLibvirtError(); + goto fallback; + } + + if (last_error && last_error->code == VIR_ERR_INVALID_ARG) { + /* try the new API again but mask non-guaranteed flags */ + unsigned int newflags = flags & (VIR_CONNECT_LIST_NETWORKS_ACTIVE | + VIR_CONNECT_LIST_NETWORKS_INACTIVE); + + vshResetLibvirtError(); + if ((ret = virConnectListAllNetworks(ctl->conn, &list->nets, + newflags)) >= 0) { + list->nnets = ret; + goto filter; + } + } + + /* there was an error during the first or second call */ + vshError(ctl, "%s", _("Failed to list networks")); + goto cleanup; + + +fallback: + /* fall back to old method (0.9.13 and older) */ + vshResetLibvirtError(); + + /* Get the number of active networks */ + if (!MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) || + MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) { + if ((nActiveNets = virConnectNumOfNetworks(ctl->conn)) < 0) { + vshError(ctl, "%s", _("Failed to get the number of active networks")); + goto cleanup; + } + } + + /* Get the number of inactive networks */ + if (!MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) || + MATCH(VIR_CONNECT_LIST_NETWORKS_INACTIVE)) { + if ((nInactiveNets = virConnectNumOfDefinedNetworks(ctl->conn)) < 0) { + vshError(ctl, "%s", _("Failed to get the number of inactive networks")); + goto cleanup; + } + } + + nAllNets = nActiveNets + nInactiveNets; + + if (nAllNets == 0) + return list; + + names = vshMalloc(ctl, sizeof(char *) * nAllNets); + + /* Retrieve a list of active network names */ + if (!MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) || + MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) { + if (virConnectListNetworks(ctl->conn, + names, nActiveNets) < 0) { + vshError(ctl, "%s", _("Failed to list active networks")); + goto cleanup; + } + } + + /* Add the inactive networks to the end of the name list */ + if (!MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) || + MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE)) { + if (virConnectListDefinedNetworks(ctl->conn, + &names[nActiveNets], + nInactiveNets) < 0) { + vshError(ctl, "%s", _("Failed to list inactive networks")); + goto cleanup; + } + } + + list->nets = vshMalloc(ctl, sizeof(virNetworkPtr) * (nAllNets)); + list->nnets = 0; + + /* get active networks */ + for (i = 0; i < nActiveNets; i++) { + if (!(net = virNetworkLookupByName(ctl->conn, names[i]))) + continue; + list->nets[list->nnets++] = net; + } + + /* get inactive networks */ + for (i = 0; i < nInactiveNets; i++) { + if (!(net = virNetworkLookupByName(ctl->conn, names[i]))) + continue; + list->nets[list->nnets++] = net; + } + + /* truncate networks that weren't found */ + deleted = nAllNets - list->nnets; + +filter: + /* filter list the list if the list was acquired by fallback means */ + for (i = 0; i < list->nnets; i++) { + net = list->nets[i]; + + /* persistence filter */ + if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT)) { + if ((persistent = virNetworkIsPersistent(net)) < 0) { + vshError(ctl, "%s", _("Failed to get network persistence info")); + goto cleanup; + } + + if (!((MATCH(VIR_CONNECT_LIST_NETWORKS_PERSISTENT) && persistent) || + (MATCH(VIR_CONNECT_LIST_NETWORKS_TRANSIENT) && !persistent))) + goto remove_entry; + } + + /* autostart filter */ + if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART)) { + if (virNetworkGetAutostart(net, &autostart) < 0) { + vshError(ctl, "%s", _("Failed to get network autostart state")); + goto cleanup; + } + + if (!((MATCH(VIR_CONNECT_LIST_NETWORKS_AUTOSTART) && autostart) || + (MATCH(VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART) && !autostart))) + goto remove_entry; + } + /* the pool matched all filters, it may stay */ + continue; + +remove_entry: + /* the pool has to be removed as it failed one of the filters */ + virNetworkFree(list->nets[i]); + list->nets[i] = NULL; + deleted++; + } + +finished: + /* sort the list */ + if (list->nets && list->nnets) + qsort(list->nets, list->nnets, + sizeof(*list->nets), vshNetworkSorter); + + /* truncate the list if filter simulation deleted entries */ + if (deleted) + VIR_SHRINK_N(list->nets, list->nnets, deleted); + + success = true; + +cleanup: + for (i = 0; i < nAllNets; i++) + VIR_FREE(names[i]); + VIR_FREE(names); + + if (!success) { + vshNetworkListFree(list); + list = NULL; + } + + return list; +} + /* * "net-list" command */ @@ -361,117 +582,73 @@ static const vshCmdInfo info_network_list[] = { static const vshCmdOptDef opts_network_list[] = { {"inactive", VSH_OT_BOOL, 0, N_("list inactive networks")}, {"all", VSH_OT_BOOL, 0, N_("list inactive & active networks")}, + {"persistent", VSH_OT_BOOL, 0, N_("list persistent networks")}, + {"transient", VSH_OT_BOOL, 0, N_("list transient networks")}, + {"autostart", VSH_OT_BOOL, 0, N_("list networks with autostart enabled")}, + {"no-autostart", VSH_OT_BOOL, 0, N_("list networks with autostart disabled")}, {NULL, 0, 0, NULL} }; static bool cmdNetworkList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { + vshNetworkListPtr list = NULL; + int i; bool inactive = vshCommandOptBool(cmd, "inactive"); bool all = vshCommandOptBool(cmd, "all"); - bool active = !inactive || all; - int maxactive = 0, maxinactive = 0, i; - char **activeNames = NULL, **inactiveNames = NULL; - inactive |= all; + bool persistent = vshCommandOptBool(cmd, "persistent"); + bool transient = vshCommandOptBool(cmd, "transient"); + bool autostart = vshCommandOptBool(cmd, "autostart"); + bool no_autostart = vshCommandOptBool(cmd, "no-autostart"); + unsigned int flags = VIR_CONNECT_LIST_NETWORKS_ACTIVE; if (!vshConnectionUsability(ctl, ctl->conn)) return false; - if (active) { - maxactive = virConnectNumOfNetworks(ctl->conn); - if (maxactive < 0) { - vshError(ctl, "%s", _("Failed to list active networks")); - return false; - } - if (maxactive) { - activeNames = vshMalloc(ctl, sizeof(char *) * maxactive); - - if ((maxactive = virConnectListNetworks(ctl->conn, activeNames, - maxactive)) < 0) { - vshError(ctl, "%s", _("Failed to list active networks")); - VIR_FREE(activeNames); - return false; - } + if (inactive) + flags = VIR_CONNECT_LIST_NETWORKS_INACTIVE; - qsort(&activeNames[0], maxactive, sizeof(char *), vshNameSorter); - } - } - if (inactive) { - maxinactive = virConnectNumOfDefinedNetworks(ctl->conn); - if (maxinactive < 0) { - vshError(ctl, "%s", _("Failed to list inactive networks")); - VIR_FREE(activeNames); - return false; - } - if (maxinactive) { - inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive); - - if ((maxinactive = - virConnectListDefinedNetworks(ctl->conn, inactiveNames, - maxinactive)) < 0) { - vshError(ctl, "%s", _("Failed to list inactive networks")); - VIR_FREE(activeNames); - VIR_FREE(inactiveNames); - return false; - } + if (all) + flags = VIR_CONNECT_LIST_NETWORKS_ACTIVE | + VIR_CONNECT_LIST_NETWORKS_INACTIVE; - qsort(&inactiveNames[0], maxinactive, sizeof(char*), vshNameSorter); - } - } - vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"), - _("Autostart")); - vshPrintExtra(ctl, "-----------------------------------------\n"); + if (persistent) + flags |= VIR_CONNECT_LIST_NETWORKS_PERSISTENT; - for (i = 0; i < maxactive; i++) { - virNetworkPtr network = - virNetworkLookupByName(ctl->conn, activeNames[i]); - const char *autostartStr; - int autostart = 0; + if (transient) + flags |= VIR_CONNECT_LIST_NETWORKS_TRANSIENT; - /* this kind of work with networks is not atomic operation */ - if (!network) { - VIR_FREE(activeNames[i]); - continue; - } + if (autostart) + flags |= VIR_CONNECT_LIST_NETWORKS_AUTOSTART; - if (virNetworkGetAutostart(network, &autostart) < 0) - autostartStr = _("no autostart"); - else - autostartStr = autostart ? _("yes") : _("no"); + if (no_autostart) + flags |= VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART; - vshPrint(ctl, "%-20s %-10s %-10s\n", - virNetworkGetName(network), - _("active"), - autostartStr); - virNetworkFree(network); - VIR_FREE(activeNames[i]); - } - for (i = 0; i < maxinactive; i++) { - virNetworkPtr network = virNetworkLookupByName(ctl->conn, inactiveNames[i]); - const char *autostartStr; - int autostart = 0; + if (!(list = vshNetworkListCollect(ctl, flags))) + return false; - /* this kind of work with networks is not atomic operation */ - if (!network) { - VIR_FREE(inactiveNames[i]); - continue; - } + vshPrintExtra(ctl, "%-20s %-10s %-13s %s\n", _("Name"), _("State"), + _("Autostart"), _("Persistent")); + vshPrintExtra(ctl, "--------------------------------------------------\n"); - if (virNetworkGetAutostart(network, &autostart) < 0) + for (i = 0; i < list->nnets; i++) { + virNetworkPtr network = list->nets[i]; + const char *autostartStr; + int is_autostart = 0; + + if (virNetworkGetAutostart(network, &is_autostart) < 0) autostartStr = _("no autostart"); else - autostartStr = autostart ? _("yes") : _("no"); - - vshPrint(ctl, "%-20s %-10s %-10s\n", - inactiveNames[i], - _("inactive"), - autostartStr); + autostartStr = is_autostart ? _("yes") : _("no"); - virNetworkFree(network); - VIR_FREE(inactiveNames[i]); + vshPrint(ctl, "%-20s %-10s %-13s %s\n", + virNetworkGetName(network), + virNetworkIsActive(network) ? _("active") : _("inactive"), + autostartStr, + virNetworkIsPersistent(network) ? _("yes") : _("no")); } - VIR_FREE(activeNames); - VIR_FREE(inactiveNames); + + vshNetworkListFree(list); return true; } diff --git a/tools/virsh.pod b/tools/virsh.pod index 7517a00..4462efa 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1909,10 +1909,20 @@ variables, and defaults to C<vi>. Returns basic information about the I<network> object. =item B<net-list> [I<--inactive> | I<--all>] + [I<--persistent>] [<--transient>] + [I<--autostart>] [<--no-autostart>] Returns the list of active networks, if I<--all> is specified this will also include defined but inactive networks, if I<--inactive> is specified only the -inactive ones will be listed. +inactive ones will be listed. You may also want to filter the returned networks +by I<--persistent> to list the persitent ones, I<--transient> to list the +transient ones, I<--autostart> to list the ones with autostart enabled, and +I<--no-autostart> to list the ones with autostart disabled. + +NOTE: When talking to older servers, this command is forced to use a series of +API calls with an inherent race, where a pool might not be listed or might appear +more than once if it changed state between calls while the list was being +collected. Newer servers do not have this problem. =item B<net-name> I<network-UUID> -- 1.7.7.3

The implementation is done manually as the generator does not support wrapping lists of C pointers into Python objects. python/libvirt-override-api.xml: Document python/libvirt-override-virConnect.py: Implement listAllNetworks. python/libvirt-override.c: Implementation for the wrapper. --- python/libvirt-override-api.xml | 6 ++++ python/libvirt-override-virConnect.py | 12 ++++++++ python/libvirt-override.c | 48 +++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 0 deletions(-) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 8a228fb..5f51fc7 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -37,6 +37,12 @@ <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> <return type='str *' info='the list of Names or None in case of error'/> </function> + <function name='virConnectListAllNetworks' file='python'> + <info>returns list of all networks</info> + <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> + <arg name='flags' type='unsigned int' info='optional flags'/> + <return type='network *' info='the list of networks or None in case of error'/> + </function> <function name='virDomainLookupByUUID' file='python'> <info>Try to lookup a domain on the given hypervisor based on its UUID.</info> <return type='virDomainPtr' info='a new domain object or NULL in case of failure'/> diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index 87a737f..85db5fe 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -218,3 +218,15 @@ retlist.append(virStoragePool(self, _obj=poolptr)) return retlist + + def listAllNetworks(self, flags): + """Returns a list of network objects""" + ret = libvirtmod.virConnectListAllNetworks(self._o, flags) + if ret is None: + raise libvirtError("virConnectListAllNetworks() failed", conn=self) + + retlist = list() + for netptr in ret: + retlist.append(virNetwork(self, _obj=netptr)) + + return retlist diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 322a8d2..5dd9657 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -2594,6 +2594,53 @@ libvirt_virConnectListDefinedNetworks(PyObject *self ATTRIBUTE_UNUSED, return py_retval; } +static PyObject * +libvirt_virConnectListAllNetworks(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *pyobj_conn; + PyObject *py_retval = NULL; + PyObject *tmp = NULL; + virConnectPtr conn; + virNetworkPtr *nets = NULL; + int c_retval = 0; + int i; + unsigned int flags; + + if (!PyArg_ParseTuple(args, (char *)"Oi:virConnectListAllNetworks", + &pyobj_conn, &flags)) + return NULL; + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virConnectListAllNetworks(conn, &nets, flags); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + + if (!(py_retval = PyList_New(c_retval))) + goto cleanup; + + for (i = 0; i < c_retval; i++) { + if (!(tmp = libvirt_virNetworkPtrWrap(nets[i])) || + PyList_SetItem(py_retval, i, tmp) < 0) { + Py_XDECREF(tmp); + Py_DECREF(py_retval); + py_retval = NULL; + goto cleanup; + } + /* python steals the pointer */ + nets[i] = NULL; + } + +cleanup: + for (i = 0; i < c_retval; i++) + if (nets[i]) + virNetworkFree(nets[i]); + VIR_FREE(nets); + return py_retval; +} + static PyObject * libvirt_virNetworkGetUUID(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { @@ -5939,6 +5986,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virConnGetLastError", libvirt_virConnGetLastError, METH_VARARGS, NULL}, {(char *) "virConnectListNetworks", libvirt_virConnectListNetworks, METH_VARARGS, NULL}, {(char *) "virConnectListDefinedNetworks", libvirt_virConnectListDefinedNetworks, METH_VARARGS, NULL}, + {(char *) "virConnectListAllNetworks", libvirt_virConnectListAllNetworks, METH_VARARGS, NULL}, {(char *) "virNetworkGetUUID", libvirt_virNetworkGetUUID, METH_VARARGS, NULL}, {(char *) "virNetworkGetUUIDString", libvirt_virNetworkGetUUIDString, METH_VARARGS, NULL}, {(char *) "virNetworkLookupByUUID", libvirt_virNetworkLookupByUUID, METH_VARARGS, NULL}, -- 1.7.7.3

This is to list the interface objects, supported filtering flags are: active|inactive. include/libvirt/libvirt.h.in: Declare enum virConnectListAllInterfaceFlags and virConnectListAllInterfaces. python/generator.py: Skip auto-generating src/driver.h: (virDrvConnectListAllInterfaces) src/libvirt.c: Implement the public API src/libvirt_public.syms: Export the symbol to public --- include/libvirt/libvirt.h.in | 13 +++++++ python/generator.py | 1 + src/driver.h | 5 +++ src/libvirt.c | 77 ++++++++++++++++++++++++++++++++++++++++- src/libvirt_public.syms | 1 + 5 files changed, 95 insertions(+), 2 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 49b2ab7..dc6c98d 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2356,6 +2356,19 @@ int virConnectNumOfDefinedInterfaces (virConnectPtr conn); int virConnectListDefinedInterfaces (virConnectPtr conn, char **const names, int maxnames); +/* + * virConnectListAllInterfaces: + * + * Flags used to filter the returned interfaces. + */ +typedef enum { + VIR_CONNECT_LIST_INTERFACES_INACTIVE = 1 << 0, + VIR_CONNECT_LIST_INTERFACES_ACTIVE = 1 << 1, +} virConnectListAllInterfacesFlags; + +int virConnectListAllInterfaces (virConnectPtr conn, + virInterfacePtr **ifaces, + unsigned int flags); virInterfacePtr virInterfaceLookupByName (virConnectPtr conn, const char *name); diff --git a/python/generator.py b/python/generator.py index 6380497..25f7f46 100755 --- a/python/generator.py +++ b/python/generator.py @@ -461,6 +461,7 @@ skip_function = ( 'virConnectListAllStoragePools', # overridden in virConnect.py 'virStoragePoolListAllVolumes', # overridden in virStoragePool.py 'virConnectListAllNetworks', # overridden in virConnect.py + 'virConnectListAllInterfaces', # overridden in virConnect.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 147a92f..52d7c32 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1153,6 +1153,10 @@ typedef int (*virDrvListDefinedInterfaces) (virConnectPtr conn, char **const names, int maxnames); +typedef int + (*virDrvListAllInterfaces) (virConnectPtr conn, + virInterfacePtr **ifaces, + unsigned int flags); typedef virInterfacePtr (*virDrvInterfaceLookupByName) (virConnectPtr conn, const char *name); @@ -1211,6 +1215,7 @@ struct _virInterfaceDriver { virDrvListInterfaces listInterfaces; virDrvNumOfDefinedInterfaces numOfDefinedInterfaces; virDrvListDefinedInterfaces listDefinedInterfaces; + virDrvListAllInterfaces listAllInterfaces; virDrvInterfaceLookupByName interfaceLookupByName; virDrvInterfaceLookupByMACString interfaceLookupByMACString; virDrvInterfaceGetXMLDesc interfaceGetXMLDesc; diff --git a/src/libvirt.c b/src/libvirt.c index 2bbcf9d..5e4ed0d 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -10529,6 +10529,67 @@ virInterfaceGetConnect (virInterfacePtr iface) } /** + * virConnectListAllInterfaces: + * @conn: Pointer to the hypervisor connection. + * @ifaces: Pointer to a variable to store the array containing the interface + * objects or NULL if the list is not required (just returns number + * of interfaces). + * @flags: bitwise-OR of virConnectListAllInterfacesFlags. + * + * Collect the list of interfaces, and allocate an array to store those + * objects. This API solves the race inherent between virConnectListInterfaces + * and virConnectListDefinedInterfaces. + * + * Normally, all interfaces are returned; however, @flags can be used to + * filter the results for a smaller list of targeted interfaces. The valid + * flags are divided into groups, where each group contains bits that + * describe mutually exclusive attributes of a interface, and where all bits + * within a group describe all possible interfaces. + * + * The only one group of @flags is VIR_CONNECT_LIST_INTERFACES_ACTIVE (up) and + * VIR_CONNECT_LIST_INTERFACES_INACTIVE (down) to fitler the interfaces by state. + * + * Returns the number of interfaces found or -1 and sets @ifaces to NULL in case + * of error. On success, the array stored into @ifaces 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 + * virStorageInterfaceFree() on each array element, then calling free() on @ifaces. + */ +int +virConnectListAllInterfaces(virConnectPtr conn, + virInterfacePtr **ifaces, + unsigned int flags) +{ + VIR_DEBUG("conn=%p, ifaces=%p, flags=%x", conn, ifaces, flags); + + virResetLastError(); + + if (ifaces) + *ifaces = NULL; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->interfaceDriver && + conn->interfaceDriver->listAllInterfaces) { + int ret; + ret = conn->interfaceDriver->listAllInterfaces(conn, ifaces, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + +/** * virConnectNumOfInterfaces: * @conn: pointer to the hypervisor connection * @@ -10573,7 +10634,13 @@ error: * Collect the list of active physical host interfaces, * and store their names in @names * - * Returns the number of interfaces found or -1 in case of error + * For more control over the results, see virConnectListAllInterfaces(). + * + * Returns the number of interfaces found or -1 in case of error. Note that + * this command is inherently racy; a interface can be started between a call + * to virConnectNumOfInterfaces() and this call; you are only guaranteed that + * all currently active interfaces were listed if the return is less than + * @maxnames. */ int virConnectListInterfaces(virConnectPtr conn, char **const names, int maxnames) @@ -10651,7 +10718,13 @@ error: * Collect the list of defined (inactive) physical host interfaces, * and store their names in @names. * - * Returns the number of interfaces found or -1 in case of error + * For more control over the results, see virConnectListAllInterfaces(). + * + * Returns the number of names provided in the array or -1 in case of error. + * Note that this command is inherently racy; a interface can be defined between + * a call to virConnectNumOfDefinedInterfaces() and this call; you are only + * guaranteed that all currently defined interfaces were listed if the return + * is less than @maxnames. The client must call free() on each returned name. */ int virConnectListDefinedInterfaces(virConnectPtr conn, diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 94ec37f..7d797e8 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -552,6 +552,7 @@ LIBVIRT_0.10.0 { virConnectListAllStoragePools; virStoragePoolListAllVolumes; virConnectListAllNetworks; + virConnectListAllInterfaces; } LIBVIRT_0.9.13; # .... define new API here using predicted next version number .... -- 1.7.7.3

The RPC generator doesn't support returning list of object yet, this patch do the work manually. * daemon/remote.c: Implemente the server side handler remoteDispatchConnectListAllInterfaces. * src/remote/remote_driver.c: Add remote driver handler remoteConnectListAllInterfaces. * src/remote/remote_protocol.x: New RPC procedure REMOTE_PROC_CONNECT_LIST_ALL_INTERFACES and structs to represent the args and ret for it. * src/remote_protocol-structs: Likewise. --- daemon/remote.c | 54 +++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 64 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 14 ++++++++- src/remote_protocol-structs | 12 ++++++++ 4 files changed, 143 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index f0313e7..81c53e9 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4111,6 +4111,60 @@ cleanup: return rv; } +static int +remoteDispatchConnectListAllInterfaces(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_connect_list_all_interfaces_args *args, + remote_connect_list_all_interfaces_ret *ret) +{ + virInterfacePtr *ifaces = NULL; + int nifaces = 0; + int i; + int rv = -1; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if ((nifaces = virConnectListAllInterfaces(priv->conn, + args->need_results ? &ifaces : NULL, + args->flags)) < 0) + goto cleanup; + + if (ifaces && nifaces) { + if (VIR_ALLOC_N(ret->ifaces.ifaces_val, nifaces) < 0) { + virReportOOMError(); + goto cleanup; + } + + ret->ifaces.ifaces_len = nifaces; + + for (i = 0; i < nifaces; i++) + make_nonnull_interface(ret->ifaces.ifaces_val + i, ifaces[i]); + } else { + ret->ifaces.ifaces_len = 0; + ret->ifaces.ifaces_val = NULL; + } + + ret->ret = nifaces; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (ifaces) { + for (i = 0; i < nifaces; i++) + virInterfaceFree(ifaces[i]); + VIR_FREE(ifaces); + } + return rv; +} + /*----- Helpers. -----*/ diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 0729a73..6d002f6 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2863,6 +2863,69 @@ done: return rv; } +static int +remoteConnectListAllInterfaces(virConnectPtr conn, + virInterfacePtr **ifaces, + unsigned int flags) +{ + int rv = -1; + int i; + virInterfacePtr *tmp_ifaces = NULL; + remote_connect_list_all_interfaces_args args; + remote_connect_list_all_interfaces_ret ret; + + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + args.need_results = !!ifaces; + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + if (call(conn, + priv, + 0, + REMOTE_PROC_CONNECT_LIST_ALL_INTERFACES, + (xdrproc_t) xdr_remote_connect_list_all_interfaces_args, + (char *) &args, + (xdrproc_t) xdr_remote_connect_list_all_interfaces_ret, + (char *) &ret) == -1) + goto done; + + if (ifaces) { + if (VIR_ALLOC_N(tmp_ifaces, ret.ifaces.ifaces_len + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < ret.ifaces.ifaces_len; i++) { + tmp_ifaces[i] = get_nonnull_interface (conn, ret.ifaces.ifaces_val[i]); + if (!tmp_ifaces[i]) { + virReportOOMError(); + goto cleanup; + } + } + *ifaces = tmp_ifaces; + tmp_ifaces = NULL; + } + + rv = ret.ret; + +cleanup: + if (tmp_ifaces) { + for (i = 0; i < ret.ifaces.ifaces_len; i++) + if (tmp_ifaces[i]) + virInterfaceFree(tmp_ifaces[i]); + VIR_FREE(tmp_ifaces); + } + + xdr_free((xdrproc_t) xdr_remote_connect_list_all_interfaces_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + /*----------------------------------------------------------------------*/ @@ -5595,6 +5658,7 @@ static virInterfaceDriver interface_driver = { .listInterfaces = remoteListInterfaces, /* 0.7.2 */ .numOfDefinedInterfaces = remoteNumOfDefinedInterfaces, /* 0.7.2 */ .listDefinedInterfaces = remoteListDefinedInterfaces, /* 0.7.2 */ + .listAllInterfaces = remoteConnectListAllInterfaces, /* 0.10.0 */ .interfaceLookupByName = remoteInterfaceLookupByName, /* 0.7.2 */ .interfaceLookupByMACString = remoteInterfaceLookupByMACString, /* 0.7.2 */ .interfaceGetXMLDesc = remoteInterfaceGetXMLDesc, /* 0.7.2 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index d095a2c..36db5b1 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2558,6 +2558,16 @@ struct remote_connect_list_all_networks_ret { unsigned int ret; }; +struct remote_connect_list_all_interfaces_args { + int need_results; + unsigned int flags; +}; + +struct remote_connect_list_all_interfaces_ret { + remote_nonnull_interface ifaces<>; + unsigned int ret; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -2887,7 +2897,9 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_HOSTNAME = 277, /* autogen autogen */ REMOTE_PROC_CONNECT_LIST_ALL_STORAGE_POOLS = 278, /* skipgen skipgen priority:high */ REMOTE_PROC_STORAGE_POOL_LIST_ALL_VOLUMES = 279, /* skipgen skipgen priority:high */ - REMOTE_PROC_CONNECT_LIST_ALL_NETWORKS = 280 /* skipgen skipgen priority:high */ + REMOTE_PROC_CONNECT_LIST_ALL_NETWORKS = 280, /* skipgen skipgen priority:high */ + + REMOTE_PROC_CONNECT_LIST_ALL_INTERFACES = 281 /* skipgen skipgen priority:high */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index fe3c627..8281991 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2015,6 +2015,17 @@ struct remote_list_all_networks_ret { } nets; u_int ret; }; +struct remote_connect_list_all_interfaces_args { + int need_results; + u_int flags; +}; +struct remote_connect_list_all_interfaces_ret { + struct { + u_int ifaces_len; + remote_nonnull_interface * ifaces_val; + } pools; + u_int ret; +}; enum remote_procedure { REMOTE_PROC_OPEN = 1, REMOTE_PROC_CLOSE = 2, @@ -2296,4 +2307,5 @@ enum remote_procedure { REMOTE_PROC_CONNECT_LIST_ALL_STORAGE_POOLS = 278, REMOTE_PROC_STORAGE_POOL_LIST_ALL_VOLUMES = 279, REMOTE_PROC_CONNECT_LIST_ALL_NETWORKS = 280, + REMOTE_PROC_CONNECT_LIST_ALL_INTERFACES = 281, }; -- 1.7.7.3

This is not that ideal as API for other objects, as it's still O(n). Because interface driver uses netcf APIs to manage the stuffs, instead of by itself. And netcf APIs don't return a object. It provides APIs like old libvirt APIs: ncf_number_of_interfaces ncf_list_interfaces ncf_lookup_by_name ...... Perhaps we should further hack netcf to let it provide an API to return the object, but it could be a later patch. And anyway, we will still befinit from the new API for the simplification, and no race like the old APIs. src/interface/netcf_driver.c: Implement listAllInterfaces --- src/interface/netcf_driver.c | 135 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 135 insertions(+), 0 deletions(-) diff --git a/src/interface/netcf_driver.c b/src/interface/netcf_driver.c index 935be66..1dae99b 100644 --- a/src/interface/netcf_driver.c +++ b/src/interface/netcf_driver.c @@ -259,6 +259,140 @@ static int interfaceListDefinedInterfaces(virConnectPtr conn, char **const names } +static int +interfaceListAllInterfaces(virConnectPtr conn, + virInterfacePtr **ifaces, + unsigned int flags) +{ + struct interface_driver *driver = conn->interfacePrivateData; + int count; + int i; + struct netcf_if *iface = NULL; + virInterfacePtr *tmp_iface_objs = NULL; + virInterfacePtr iface_obj = NULL; + unsigned int status; + int niface_objs = 0; + int ret = -1; + char **names; + + virCheckFlags(VIR_CONNECT_LIST_INTERFACES_ACTIVE | + VIR_CONNECT_LIST_INTERFACES_INACTIVE, -1); + + interfaceDriverLock(driver); + + /* List all interfaces, in case of we might support new filter flags + * except active|inactive in future. + */ + count = ncf_num_of_interfaces(driver->netcf, NETCF_IFACE_ACTIVE | + NETCF_IFACE_INACTIVE); + if (count < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to get number of host interfaces: %s%s%s"), + errmsg, details ? " - " : "", + details ? details : ""); + return -1; + } + + if (count == 0) + return 0; + + if (VIR_ALLOC_N(names, count) < 0) { + virReportOOMError(); + return -1; + } + + if ((count = ncf_list_interfaces(driver->netcf, count, names, + NETCF_IFACE_ACTIVE | + NETCF_IFACE_INACTIVE)) < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to list host interfaces: %s%s%s"), + errmsg, details ? " - " : "", + details ? details : ""); + goto cleanup; + } + + if (ifaces) { + if (VIR_ALLOC_N(tmp_iface_objs, count + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + } + + for (i = 0; i < count; i++) { + iface = ncf_lookup_by_name(driver->netcf, names[i]); + if (!iface) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + if (errcode != NETCF_NOERROR) { + virReportError(netcf_to_vir_err(errcode), + _("couldn't find interface named '%s': %s%s%s"), + names[i], errmsg, + details ? " - " : "", details ? details : ""); + } else { + virReportError(VIR_ERR_NO_INTERFACE, + _("couldn't find interface named '%s'"), names[i]); + } + goto cleanup; + } + + if (ncf_if_status(iface, &status) < 0) { + const char *errmsg, *details; + int errcode = ncf_error(driver->netcf, &errmsg, &details); + virReportError(netcf_to_vir_err(errcode), + _("failed to get status of interface %s: %s%s%s"), + names[i], errmsg, details ? " - " : "", + details ? details : ""); + goto cleanup; + } + + /* XXX: Filter the result, need to be splitted once new fitler flags + * except active|inactive are supported. + */ + if (((status & NETCF_IFACE_ACTIVE) && + (flags & VIR_CONNECT_LIST_INTERFACES_ACTIVE)) || + ((status & NETCF_IFACE_INACTIVE) && + (flags & VIR_CONNECT_LIST_INTERFACES_INACTIVE))) { + if (ifaces) { + iface_obj = virGetInterface(conn, ncf_if_name(iface), + ncf_if_mac_string(iface)); + tmp_iface_objs[niface_objs] = iface_obj; + } + niface_objs++; + } + } + + if (tmp_iface_objs) { + /* trim the array to the final size */ + ignore_value(VIR_REALLOC_N(tmp_iface_objs, niface_objs + 1)); + *ifaces = tmp_iface_objs; + tmp_iface_objs = NULL; + } + + ret = niface_objs; + +cleanup: + ncf_if_free(iface); + + for (i = 0; i < count; i++) + VIR_FREE(names[i]); + VIR_FREE(names); + + if (tmp_iface_objs) { + for (i = 0; i < niface_objs; i++) { + if (tmp_iface_objs[i]) + virInterfaceFree(tmp_iface_objs[i]); + } + } + + interfaceDriverUnlock(driver); + return ret; +} + + static virInterfacePtr interfaceLookupByName(virConnectPtr conn, const char *name) { @@ -642,6 +776,7 @@ static virInterfaceDriver interfaceDriver = { .listInterfaces = interfaceListInterfaces, /* 0.7.0 */ .numOfDefinedInterfaces = interfaceNumOfDefinedInterfaces, /* 0.7.0 */ .listDefinedInterfaces = interfaceListDefinedInterfaces, /* 0.7.0 */ + .listAllInterfaces = interfaceListAllInterfaces, /* 0.10.0 */ .interfaceLookupByName = interfaceLookupByName, /* 0.7.0 */ .interfaceLookupByMACString = interfaceLookupByMACString, /* 0.7.0 */ .interfaceGetXMLDesc = interfaceGetXMLDesc, /* 0.7.0 */ -- 1.7.7.3

tools/virsh-interface.c: * vshInterfaceSorter to sort interfaces by name * vshInterfaceListFree to free the interface objects list. * vshInterfaceListCollect to collect the interface objects, trying to use new API first, fall back to older APIs if it's not supported. --- tools/virsh-interface.c | 259 +++++++++++++++++++++++++++++++++-------------- 1 files changed, 184 insertions(+), 75 deletions(-) diff --git a/tools/virsh-interface.c b/tools/virsh-interface.c index 12019b4..5e70498 100644 --- a/tools/virsh-interface.c +++ b/tools/virsh-interface.c @@ -124,6 +124,174 @@ cleanup: return ret; } +static int +vshInterfaceSorter(const void *a, const void *b) +{ + virInterfacePtr *ia = (virInterfacePtr *) a; + virInterfacePtr *ib = (virInterfacePtr *) b; + + if (*ia && !*ib) + return -1; + + if (!*ia) + return *ib != NULL; + + return vshStrcasecmp(virInterfaceGetName(*ia), + virInterfaceGetName(*ib)); +} + +struct vshInterfaceList { + virInterfacePtr *ifaces; + size_t nifaces; +}; +typedef struct vshInterfaceList *vshInterfaceListPtr; + +static void +vshInterfaceListFree(vshInterfaceListPtr list) +{ + int i; + + if (list && list->nifaces) { + for (i = 0; i < list->nifaces; i++) { + if (list->ifaces[i]) + virInterfaceFree(list->ifaces[i]); + } + VIR_FREE(list->ifaces); + } + VIR_FREE(list); +} + +static vshInterfaceListPtr +vshInterfaceListCollect(vshControl *ctl, + unsigned int flags) +{ + vshInterfaceListPtr list = vshMalloc(ctl, sizeof(*list)); + int i; + int ret; + char **activeNames = NULL; + char **inactiveNames = NULL; + virInterfacePtr iface; + bool success = false; + size_t deleted = 0; + int nActiveIfaces = 0; + int nInactiveIfaces = 0; + int nAllIfaces = 0; + + /* try the list with flags support (0.10.0 and later) */ + if ((ret = virConnectListAllInterfaces(ctl->conn, + &list->ifaces, + flags)) >= 0) { + list->nifaces = ret; + goto finished; + } + + /* check if the command is actually supported */ + if (last_error && last_error->code == VIR_ERR_NO_SUPPORT) { + vshResetLibvirtError(); + goto fallback; + } + + /* there was an error during the first or second call */ + vshError(ctl, "%s", _("Failed to list interfaces")); + goto cleanup; + + +fallback: + /* fall back to old method (0.9.13 and older) */ + vshResetLibvirtError(); + + if (flags & VIR_CONNECT_LIST_INTERFACES_ACTIVE) { + nActiveIfaces = virConnectNumOfInterfaces(ctl->conn); + if (nActiveIfaces < 0) { + vshError(ctl, "%s", _("Failed to list active interfaces")); + goto cleanup; + } + if (nActiveIfaces) { + activeNames = vshMalloc(ctl, sizeof(char *) * nActiveIfaces); + + if ((nActiveIfaces = virConnectListInterfaces(ctl->conn, activeNames, + nActiveIfaces)) < 0) { + vshError(ctl, "%s", _("Failed to list active interfaces")); + goto cleanup; + } + } + } + + if (flags & VIR_CONNECT_LIST_INTERFACES_INACTIVE) { + nInactiveIfaces = virConnectNumOfDefinedInterfaces(ctl->conn); + if (nInactiveIfaces < 0) { + vshError(ctl, "%s", _("Failed to list inactive interfaces")); + goto cleanup; + } + if (nInactiveIfaces) { + inactiveNames = vshMalloc(ctl, sizeof(char *) * nInactiveIfaces); + + if ((nInactiveIfaces = + virConnectListDefinedInterfaces(ctl->conn, inactiveNames, + nInactiveIfaces)) < 0) { + vshError(ctl, "%s", _("Failed to list inactive interfaces")); + goto cleanup; + } + } + } + + nAllIfaces = nActiveIfaces + nInactiveIfaces; + if (nAllIfaces == 0) { + VIR_FREE(activeNames); + VIR_FREE(inactiveNames); + return list; + } + + list->ifaces = vshMalloc(ctl, sizeof(virInterfacePtr) * (nAllIfaces)); + list->nifaces = 0; + + /* get active interfaces */ + for (i = 0; i < nActiveIfaces; i++) { + if (!(iface = virInterfaceLookupByName(ctl->conn, activeNames[i]))) + continue; + list->ifaces[list->nifaces++] = iface; + } + + /* get inactive interfaces */ + for (i = 0; i < nInactiveIfaces; i++) { + if (!(iface = virInterfaceLookupByName(ctl->conn, inactiveNames[i]))) + continue; + list->ifaces[list->nifaces++] = iface; + } + + /* truncate interfaces that weren't found */ + deleted = nAllIfaces - list->nifaces; + +finished: + /* sort the list */ + if (list->ifaces && list->nifaces) + qsort(list->ifaces, list->nifaces, + sizeof(*list->ifaces), vshInterfaceSorter); + + /* truncate the list if filter simulation deleted entries */ + if (deleted) + VIR_SHRINK_N(list->ifaces, list->nifaces, deleted); + + success = true; + +cleanup: + for (i = 0; i < nActiveIfaces; i++) + VIR_FREE(activeNames[i]); + + for (i = 0; i < nInactiveIfaces; i++) + VIR_FREE(inactiveNames[i]); + + VIR_FREE(activeNames); + VIR_FREE(inactiveNames); + + if (!success) { + vshInterfaceListFree(list); + list = NULL; + } + + return list; +} + /* * "iface-list" command */ @@ -138,102 +306,43 @@ static const vshCmdOptDef opts_interface_list[] = { {"all", VSH_OT_BOOL, 0, N_("list inactive & active interfaces")}, {NULL, 0, 0, NULL} }; + static bool cmdInterfaceList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { bool inactive = vshCommandOptBool(cmd, "inactive"); bool all = vshCommandOptBool(cmd, "all"); - bool active = !inactive || all; - int maxactive = 0, maxinactive = 0, i; - char **activeNames = NULL, **inactiveNames = NULL; - inactive |= all; + unsigned int flags = VIR_CONNECT_LIST_INTERFACES_ACTIVE; + vshInterfaceListPtr list = NULL; + int i; + + if (inactive) + flags = VIR_CONNECT_LIST_INTERFACES_INACTIVE; + if (all) + flags = VIR_CONNECT_LIST_INTERFACES_INACTIVE | + VIR_CONNECT_LIST_INTERFACES_ACTIVE; if (!vshConnectionUsability(ctl, ctl->conn)) return false; - if (active) { - maxactive = virConnectNumOfInterfaces(ctl->conn); - if (maxactive < 0) { - vshError(ctl, "%s", _("Failed to list active interfaces")); - return false; - } - if (maxactive) { - activeNames = vshMalloc(ctl, sizeof(char *) * maxactive); - - if ((maxactive = virConnectListInterfaces(ctl->conn, activeNames, - maxactive)) < 0) { - vshError(ctl, "%s", _("Failed to list active interfaces")); - VIR_FREE(activeNames); - return false; - } - - qsort(&activeNames[0], maxactive, sizeof(char *), vshNameSorter); - } - } - if (inactive) { - maxinactive = virConnectNumOfDefinedInterfaces(ctl->conn); - if (maxinactive < 0) { - vshError(ctl, "%s", _("Failed to list inactive interfaces")); - VIR_FREE(activeNames); - return false; - } - if (maxinactive) { - inactiveNames = vshMalloc(ctl, sizeof(char *) * maxinactive); - - if ((maxinactive = - virConnectListDefinedInterfaces(ctl->conn, inactiveNames, - maxinactive)) < 0) { - vshError(ctl, "%s", _("Failed to list inactive interfaces")); - VIR_FREE(activeNames); - VIR_FREE(inactiveNames); - return false; - } + if (!(list = vshInterfaceListCollect(ctl, flags))) + return false; - qsort(&inactiveNames[0], maxinactive, sizeof(char*), vshNameSorter); - } - } vshPrintExtra(ctl, "%-20s %-10s %s\n", _("Name"), _("State"), _("MAC Address")); vshPrintExtra(ctl, "--------------------------------------------\n"); - for (i = 0; i < maxactive; i++) { - virInterfacePtr iface = - virInterfaceLookupByName(ctl->conn, activeNames[i]); - - /* this kind of work with interfaces is not atomic */ - if (!iface) { - VIR_FREE(activeNames[i]); - continue; - } + for (i = 0; i < list->nifaces; i++) { + virInterfacePtr iface = list->ifaces[i]; vshPrint(ctl, "%-20s %-10s %s\n", virInterfaceGetName(iface), - _("active"), + virInterfaceIsActive(iface) ? _("active") : _("inactive"), virInterfaceGetMACString(iface)); - virInterfaceFree(iface); - VIR_FREE(activeNames[i]); } - for (i = 0; i < maxinactive; i++) { - virInterfacePtr iface = - virInterfaceLookupByName(ctl->conn, inactiveNames[i]); - - /* this kind of work with interfaces is not atomic */ - if (!iface) { - VIR_FREE(inactiveNames[i]); - continue; - } - vshPrint(ctl, "%-20s %-10s %s\n", - virInterfaceGetName(iface), - _("inactive"), - virInterfaceGetMACString(iface)); - virInterfaceFree(iface); - VIR_FREE(inactiveNames[i]); - } - VIR_FREE(activeNames); - VIR_FREE(inactiveNames); + vshInterfaceListFree(list); return true; - } /* -- 1.7.7.3

The implementation is done manually as the generator does not support wrapping lists of C pointers into Python objects. python/libvirt-override-api.xml: Document python/libvirt-override-virConnect.py: * New file, includes implementation of listAllInterfaces. python/libvirt-override.c: Implementation for the wrapper. --- python/libvirt-override-api.xml | 6 ++++ python/libvirt-override-virConnect.py | 12 ++++++++ python/libvirt-override.c | 48 +++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 0 deletions(-) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index 5f51fc7..ab6f407 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -416,6 +416,12 @@ <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> <return type='str *' info='the list of Names of None in case of error'/> </function> + <function name='virConnectListAllInterfaces' file='python'> + <info>returns list of all interfaces</info> + <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> + <arg name='flags' type='unsigned int' info='optional flags'/> + <return type='interface *' info='the list of interfaces or None in case of error'/> + </function> <function name='virConnectBaselineCPU' file='python'> <info>Computes the most feature-rich CPU which is compatible with all given host CPUs.</info> <return type='char *' info='XML description of the computed CPU or NULL on error.'/> diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index 85db5fe..ffa1a3c 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -230,3 +230,15 @@ retlist.append(virNetwork(self, _obj=netptr)) return retlist + + def listAllInterfaces(self, flags): + """Returns a list of interface objects""" + ret = libvirtmod.virConnectListAllInterfaces(self._o, flags) + if ret is None: + raise libvirtError("virConnectListAllInterfaces() failed", conn=self) + + retlist = list() + for ifaceptr in ret: + retlist.append(virInterface(self, _obj=ifaceptr)) + + return retlist diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 5dd9657..98ed40f 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3824,6 +3824,53 @@ libvirt_virConnectListDefinedInterfaces(PyObject *self ATTRIBUTE_UNUSED, static PyObject * +libvirt_virConnectListAllInterfaces(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *pyobj_conn; + PyObject *py_retval = NULL; + PyObject *tmp = NULL; + virConnectPtr conn; + virInterfacePtr *ifaces = NULL; + int c_retval = 0; + int i; + unsigned int flags; + + if (!PyArg_ParseTuple(args, (char *)"Oi:virConnectListAllInterfaces", + &pyobj_conn, &flags)) + return NULL; + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virConnectListAllInterfaces(conn, &ifaces, flags); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + + if (!(py_retval = PyList_New(c_retval))) + goto cleanup; + + for (i = 0; i < c_retval; i++) { + if (!(tmp = libvirt_virInterfacePtrWrap(ifaces[i])) || + PyList_SetItem(py_retval, i, tmp) < 0) { + Py_XDECREF(tmp); + Py_DECREF(py_retval); + py_retval = NULL; + goto cleanup; + } + /* python steals the pointer */ + ifaces[i] = NULL; + } + +cleanup: + for (i = 0; i < c_retval; i++) + if (ifaces[i]) + virInterfaceFree(ifaces[i]); + VIR_FREE(ifaces); + return py_retval; +} + +static PyObject * libvirt_virConnectBaselineCPU(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { PyObject *pyobj_conn; @@ -6045,6 +6092,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virConnectListNWFilters", libvirt_virConnectListNWFilters, METH_VARARGS, NULL}, {(char *) "virConnectListInterfaces", libvirt_virConnectListInterfaces, METH_VARARGS, NULL}, {(char *) "virConnectListDefinedInterfaces", libvirt_virConnectListDefinedInterfaces, METH_VARARGS, NULL}, + {(char *) "virConnectListAllInterfaces", libvirt_virConnectListAllInterfaces, METH_VARARGS, NULL}, {(char *) "virConnectBaselineCPU", libvirt_virConnectBaselineCPU, METH_VARARGS, NULL}, {(char *) "virDomainGetJobInfo", libvirt_virDomainGetJobInfo, METH_VARARGS, NULL}, {(char *) "virDomainSnapshotListNames", libvirt_virDomainSnapshotListNames, METH_VARARGS, NULL}, -- 1.7.7.3

This is to list the node device objects, supports to filter the results by capability types. include/libvirt/libvirt.h.in: Declare enum virConnectListAllNodeDeviceFlags and virConnectListAllNodeDevices. python/generator.py: Skip auto-generating src/driver.h: (virDrvConnectListAllNodeDevices) src/libvirt.c: Implement the public API src/libvirt_public.syms: Export the symbol to public --- include/libvirt/libvirt.h.in | 25 +++++++++++++++++ python/generator.py | 1 + src/driver.h | 4 +++ src/libvirt.c | 62 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 5 files changed, 93 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index dc6c98d..fcade4d 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2804,6 +2804,31 @@ int virNodeListDevices (virConnectPtr conn, char **const names, int maxnames, unsigned int flags); +/* + * virConnectListAllNodeDevices: + * + * Flags used to filter the returned node devices. Flags in each group + * are exclusive. + */ +typedef enum { + /* Reserved the first 6 bits for the possibility of persistent + * node device support in future. + */ + + VIR_CONNECT_LIST_NODE_DEVICES_CAP_SYSTEM = 1 << 6, + VIR_CONNECT_LIST_NODE_DEVICES_CAP_PCI_DEV = 1 << 7, + VIR_CONNECT_LIST_NODE_DEVICES_CAP_USB_DEV = 1 << 8, + VIR_CONNECT_LIST_NODE_DEVICES_CAP_USB_INTERFACE = 1 << 9, + VIR_CONNECT_LIST_NODE_DEVICES_CAP_NET = 1 << 10, + VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_HOST = 1 << 11, + VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_TARGET = 1 << 12, + VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI = 1 << 13, + VIR_CONNECT_LIST_NODE_DEVICES_CAP_STORAGE = 1 << 14, +} virConnectListAllNodeDeviceFlags; + +int virConnectListAllNodeDevices (virConnectPtr conn, + virNodeDevicePtr **devices, + unsigned int flags); virNodeDevicePtr virNodeDeviceLookupByName (virConnectPtr conn, const char *name); diff --git a/python/generator.py b/python/generator.py index 25f7f46..f63730b 100755 --- a/python/generator.py +++ b/python/generator.py @@ -462,6 +462,7 @@ skip_function = ( 'virStoragePoolListAllVolumes', # overridden in virStoragePool.py 'virConnectListAllNetworks', # overridden in virConnect.py 'virConnectListAllInterfaces', # overridden in virConnect.py + 'virConnectListAllNodeDevices', # overridden in virConnect.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 52d7c32..3cfd499 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1463,6 +1463,9 @@ typedef int (*virDevMonListDevices)(virConnectPtr conn, char **const names, int maxnames, unsigned int flags); +typedef int (*virDevMonListAllNodeDevices)(virConnectPtr conn, + virNodeDevicePtr **devices, + unsigned int flags); typedef virNodeDevicePtr (*virDevMonDeviceLookupByName)(virConnectPtr conn, const char *name); @@ -1496,6 +1499,7 @@ struct _virDeviceMonitor { virDrvClose close; virDevMonNumOfDevices numOfDevices; virDevMonListDevices listDevices; + virDevMonListAllNodeDevices listAllNodeDevices; virDevMonDeviceLookupByName deviceLookupByName; virDevMonDeviceGetXMLDesc deviceGetXMLDesc; virDevMonDeviceGetParent deviceGetParent; diff --git a/src/libvirt.c b/src/libvirt.c index 5e4ed0d..9fa76f3 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -13595,6 +13595,66 @@ error: return -1; } +/** + * virConnectListAllNodeDevices: + * @conn: Pointer to the hypervisor connection. + * @devices: Pointer to a variable to store the array containing the node + * device objects or NULL if the list is not required (just returns + * number of node devices). + * @flags: bitwise-OR of virConnectListAllNodeDevices. + * + * Collect the list of node devices, and allocate an array to store those + * objects. + * + * Normally, all node devices are returned; however, @flags can be used to + * filter the results for a smaller list of targeted node devices. The valid + * flags are divided into groups, where each group contains bits that + * describe mutually exclusive attributes of a node device, and where all bits + * within a group describe all possible node devices. + * + * Only one group of the @flags is supported. It supports to filter the node + * devices by capability type. + * + * Returns the number of node devices found or -1 and sets @devices to NULL in + * case of error. On success, the array stored into @devices 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 + * virNodeDeviceFree() on each array element, then calling free() on + * @devices. + */ +int +virConnectListAllNodeDevices(virConnectPtr conn, + virNodeDevicePtr **devices, + unsigned int flags) +{ + VIR_DEBUG("conn=%p, devices=%p, flags=%x", conn, devices, flags); + + virResetLastError(); + + if (devices) + *devices = NULL; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->deviceMonitor && + conn->deviceMonitor->listAllNodeDevices) { + int ret; + ret = conn->deviceMonitor->listAllNodeDevices(conn, devices, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} /** * virNodeListDevices: @@ -13606,6 +13666,8 @@ error: * * Collect the list of node devices, and store their names in @names * + * For more control over the results, see virConnectListAllNodeDevices(). + * * If the optional 'cap' argument is non-NULL, then the count * will be restricted to devices with the specified capability * diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 7d797e8..05b32ee 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -553,6 +553,7 @@ LIBVIRT_0.10.0 { virStoragePoolListAllVolumes; virConnectListAllNetworks; virConnectListAllInterfaces; + virConnectListAllNodeDevices; } LIBVIRT_0.9.13; # .... define new API here using predicted next version number .... -- 1.7.7.3

The RPC generator doesn't support returning list of object yet, this patch do the work manually. * daemon/remote.c: Implemente the server side handler remoteDispatchConnectListAllNodeDevices. * src/remote/remote_driver.c: Add remote driver handler remoteConnectListAllNodeDevices. * src/remote/remote_protocol.x: New RPC procedure REMOTE_PROC_CONNECT_LIST_ALL_INTERFACES and --- daemon/remote.c | 53 ++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 64 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 13 ++++++++- src/remote_protocol-structs | 12 ++++++++ 4 files changed, 141 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 81c53e9..17ddc2e 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4165,6 +4165,59 @@ cleanup: return rv; } +static int +remoteDispatchConnectListAllNodeDevices(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_connect_list_all_node_devices_args *args, + remote_connect_list_all_node_devices_ret *ret) +{ + virNodeDevicePtr *devices = NULL; + int ndevices = 0; + int i; + int rv = -1; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if ((ndevices = virConnectListAllNodeDevices(priv->conn, + args->need_results ? &devices : NULL, + args->flags)) < 0) + goto cleanup; + + if (devices && ndevices) { + if (VIR_ALLOC_N(ret->devices.devices_val, ndevices) < 0) { + virReportOOMError(); + goto cleanup; + } + + ret->devices.devices_len = ndevices; + + for (i = 0; i < ndevices; i++) + make_nonnull_node_device(ret->devices.devices_val + i, devices[i]); + } else { + ret->devices.devices_len = 0; + ret->devices.devices_val = NULL; + } + + ret->ret = ndevices; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (devices) { + for (i = 0; i < ndevices; i++) + virNodeDeviceFree(devices[i]); + VIR_FREE(devices); + } + return rv; +} /*----- Helpers. -----*/ diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 6d002f6..a0bdb32 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2926,6 +2926,69 @@ done: return rv; } +static int +remoteConnectListAllNodeDevices(virConnectPtr conn, + virNodeDevicePtr **devices, + unsigned int flags) +{ + int rv = -1; + int i; + virNodeDevicePtr *tmp_devices = NULL; + remote_connect_list_all_node_devices_args args; + remote_connect_list_all_node_devices_ret ret; + + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + args.need_results = !!devices; + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + if (call(conn, + priv, + 0, + REMOTE_PROC_CONNECT_LIST_ALL_NODE_DEVICES, + (xdrproc_t) xdr_remote_connect_list_all_node_devices_args, + (char *) &args, + (xdrproc_t) xdr_remote_connect_list_all_node_devices_ret, + (char *) &ret) == -1) + goto done; + + if (devices) { + if (VIR_ALLOC_N(tmp_devices, ret.devices.devices_len + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < ret.devices.devices_len; i++) { + tmp_devices[i] = get_nonnull_node_device(conn, ret.devices.devices_val[i]); + if (!tmp_devices[i]) { + virReportOOMError(); + goto cleanup; + } + } + *devices = tmp_devices; + tmp_devices = NULL; + } + + rv = ret.ret; + +cleanup: + if (tmp_devices) { + for (i = 0; i < ret.devices.devices_len; i++) + if (tmp_devices[i]) + virNodeDeviceFree(tmp_devices[i]); + VIR_FREE(tmp_devices); + } + + xdr_free((xdrproc_t) xdr_remote_connect_list_all_node_devices_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + /*----------------------------------------------------------------------*/ @@ -5740,6 +5803,7 @@ static virDeviceMonitor dev_monitor = { .close = remoteDevMonClose, /* 0.5.0 */ .numOfDevices = remoteNodeNumOfDevices, /* 0.5.0 */ .listDevices = remoteNodeListDevices, /* 0.5.0 */ + .listAllNodeDevices = remoteConnectListAllNodeDevices, /* 0.10.0 */ .deviceLookupByName = remoteNodeDeviceLookupByName, /* 0.5.0 */ .deviceGetXMLDesc = remoteNodeDeviceGetXMLDesc, /* 0.5.0 */ .deviceGetParent = remoteNodeDeviceGetParent, /* 0.5.0 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 36db5b1..4a7116c 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2568,6 +2568,16 @@ struct remote_connect_list_all_interfaces_ret { unsigned int ret; }; +struct remote_connect_list_all_node_devices_args { + int need_results; + unsigned int flags; +}; + +struct remote_connect_list_all_node_devices_ret { + remote_nonnull_node_device devices<>; + unsigned int ret; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -2899,7 +2909,8 @@ enum remote_procedure { REMOTE_PROC_STORAGE_POOL_LIST_ALL_VOLUMES = 279, /* skipgen skipgen priority:high */ REMOTE_PROC_CONNECT_LIST_ALL_NETWORKS = 280, /* skipgen skipgen priority:high */ - REMOTE_PROC_CONNECT_LIST_ALL_INTERFACES = 281 /* skipgen skipgen priority:high */ + REMOTE_PROC_CONNECT_LIST_ALL_INTERFACES = 281, /* skipgen skipgen priority:high */ + REMOTE_PROC_CONNECT_LIST_ALL_NODE_DEVICES = 282 /* skipgen skipgen priority:high */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 8281991..aae8652 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2026,6 +2026,17 @@ struct remote_connect_list_all_interfaces_ret { } pools; u_int ret; }; +struct remote_connect_list_all_node_devices_args { + int need_results; + u_int flags; +}; +struct remote_connect_list_all_node_devices_ret { + struct { + u_int devices_len; + remote_nonnull_node_device * devices_val; + } devices; + u_int ret; +}; enum remote_procedure { REMOTE_PROC_OPEN = 1, REMOTE_PROC_CLOSE = 2, @@ -2308,4 +2319,5 @@ enum remote_procedure { REMOTE_PROC_STORAGE_POOL_LIST_ALL_VOLUMES = 279, REMOTE_PROC_CONNECT_LIST_ALL_NETWORKS = 280, REMOTE_PROC_CONNECT_LIST_ALL_INTERFACES = 281, + REMOTE_PROC_CONNECT_LIST_ALL_NODE_DEVICES = 282, }; -- 1.7.7.3

src/conf/node_device_conf.h: * New macro VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_CAP * Declare virNodeDeviceList src/conf/node_device_conf.c: * New helpers virNodeDeviceCapMatch, virNodeDeviceMatch. virNodeDeviceCapMatch looks up the list of all the caps the device support, to see if the device support the cap type. * Implement virNodeDeviceList src/libvirt_private.syms: * Export virNodeDeviceList * Export virNodeDevCapTypeFromString --- src/conf/node_device_conf.c | 103 +++++++++++++++++++++++++++++++++++++++++++ src/conf/node_device_conf.h | 16 +++++++ src/libvirt_private.syms | 2 + 3 files changed, 121 insertions(+), 0 deletions(-) diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 048c70c..60462b8 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -1432,3 +1432,106 @@ void virNodeDeviceObjUnlock(virNodeDeviceObjPtr obj) { virMutexUnlock(&obj->lock); } + +static bool +virNodeDeviceCapMatch(virNodeDeviceObjPtr devobj, + int type) +{ + virNodeDevCapsDefPtr cap = NULL; + + for (cap = devobj->def->caps; cap; cap = cap->next) { + if (type == cap->type) + return true; + } + + return false; +} + +#define MATCH(FLAG) (flags & (FLAG)) +static bool +virNodeDeviceMatch(virNodeDeviceObjPtr devobj, + unsigned int flags) +{ + /* filter by cap type */ + if (MATCH(VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_CAP)) { + if (!((MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_SYSTEM) && + virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_SYSTEM)) || + (MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_PCI_DEV) && + virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_PCI_DEV)) || + (MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_USB_DEV) && + virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_USB_DEV)) || + (MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_USB_INTERFACE) && + virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_USB_INTERFACE)) || + (MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_NET) && + virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_NET)) || + (MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_HOST) && + virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_SCSI_HOST)) || + (MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_TARGET) && + virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_SCSI_TARGET)) || + (MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI) && + virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_SCSI)) || + (MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_STORAGE) && + virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_STORAGE)))) + return false; + } + + return true; +} +#undef MATCH + +int +virNodeDeviceList(virConnectPtr conn, + virNodeDeviceObjList devobjs, + virNodeDevicePtr **devices, + unsigned int flags) +{ + virNodeDevicePtr *tmp_devices = NULL; + virNodeDevicePtr device = NULL; + int ndevices = 0; + int ret = -1; + int i; + + if (devices) { + if (VIR_ALLOC_N(tmp_devices, devobjs.count + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + } + + for (i = 0; i < devobjs.count; i++) { + virNodeDeviceObjPtr devobj = devobjs.objs[i]; + virNodeDeviceObjLock(devobj); + if (virNodeDeviceMatch(devobj, flags)) { + if (devices) { + if (!(device = virGetNodeDevice(conn, + devobj->def->name))) { + virNodeDeviceObjUnlock(devobj); + goto cleanup; + } + tmp_devices[ndevices] = device; + } + ndevices++; + } + virNodeDeviceObjUnlock(devobj); + } + + if (tmp_devices) { + /* trim the array to the final size */ + ignore_value(VIR_REALLOC_N(tmp_devices, ndevices + 1)); + *devices = tmp_devices; + tmp_devices = NULL; + } + + ret = ndevices; + +cleanup: + if (tmp_devices) { + for (i = 0; i < ndevices; i++) { + if (tmp_devices[i]) + virNodeDeviceFree(tmp_devices[i]); + } + } + + VIR_FREE(tmp_devices); + return ret; +} diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index 41c9fcc..b8ee881 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -261,4 +261,20 @@ void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps); void virNodeDeviceObjLock(virNodeDeviceObjPtr obj); void virNodeDeviceObjUnlock(virNodeDeviceObjPtr obj); +# define VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_CAP \ + (VIR_CONNECT_LIST_NODE_DEVICES_CAP_SYSTEM | \ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_PCI_DEV | \ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_USB_DEV | \ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_USB_INTERFACE | \ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_NET | \ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_HOST | \ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_TARGET | \ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI | \ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_STORAGE) + +int virNodeDeviceList(virConnectPtr conn, + virNodeDeviceObjList devobjs, + virNodeDevicePtr **devices, + unsigned int flags); + #endif /* __VIR_NODE_DEVICE_CONF_H__ */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ec337f9..49da2e9 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -829,6 +829,7 @@ virPortGroupFindByName; # node_device_conf.h +virNodeDevCapTypeFromString; virNodeDevCapTypeToString; virNodeDevCapsDefFree; virNodeDeviceAssignDef; @@ -842,6 +843,7 @@ virNodeDeviceFindBySysfsPath; virNodeDeviceGetParentHost; virNodeDeviceGetWWNs; virNodeDeviceHasCap; +virNodeDeviceList; virNodeDeviceObjListFree; virNodeDeviceObjLock; virNodeDeviceObjRemove; -- 1.7.7.3

This simply implements listAllNodeDevices using helper virNodeDeviceList src/node_device/node_device_driver.h: * Declare nodeListAllNodeDevices. src/node_device/node_device_driver.c: * Implement nodeListAllNodeDevices. src/node_device/node_device_hal.c: * Hook listAllNodeDevices to nodeListAllNodeDevices. src/node_device/node_device_udev.c * Hook listAllNodeDevices to nodeListAllNodeDevices. --- src/node_device/node_device_driver.c | 15 +++++++++++++++ src/node_device/node_device_driver.h | 3 +++ src/node_device/node_device_hal.c | 1 + src/node_device/node_device_udev.c | 1 + 4 files changed, 20 insertions(+), 0 deletions(-) diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c index d44924c..4c62707 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -183,6 +183,21 @@ nodeListDevices(virConnectPtr conn, return -1; } +int +nodeListAllNodeDevices(virConnectPtr conn, + virNodeDevicePtr **devices, + unsigned int flags) +{ + virDeviceMonitorStatePtr driver = conn->devMonPrivateData; + int ret = -1; + + virCheckFlags(VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_CAP, -1); + + nodeDeviceLock(driver); + ret = virNodeDeviceList(conn, driver->devs, devices, flags); + nodeDeviceUnlock(driver); + return ret; +} virNodeDevicePtr nodeDeviceLookupByName(virConnectPtr conn, const char *name) diff --git a/src/node_device/node_device_driver.h b/src/node_device/node_device_driver.h index 6f680a5..b34e1af 100644 --- a/src/node_device/node_device_driver.h +++ b/src/node_device/node_device_driver.h @@ -73,6 +73,9 @@ int read_wwn_linux(int host, const char *file, char **wwn); int nodeNumOfDevices(virConnectPtr conn, const char *cap, unsigned int flags); int nodeListDevices(virConnectPtr conn, const char *cap, char **const names, int maxnames, unsigned int flags); +int nodeListAllNodeDevices(virConnectPtr conn, + virNodeDevicePtr **devices, + unsigned int flags); virNodeDevicePtr nodeDeviceLookupByName(virConnectPtr conn, const char *name); char *nodeDeviceGetXMLDesc(virNodeDevicePtr dev, unsigned int flags); char *nodeDeviceGetParent(virNodeDevicePtr dev); diff --git a/src/node_device/node_device_hal.c b/src/node_device/node_device_hal.c index 9d63a29..8cbe7dd 100644 --- a/src/node_device/node_device_hal.c +++ b/src/node_device/node_device_hal.c @@ -770,6 +770,7 @@ static virDeviceMonitor halDeviceMonitor = { .close = halNodeDrvClose, /* 0.5.0 */ .numOfDevices = nodeNumOfDevices, /* 0.5.0 */ .listDevices = nodeListDevices, /* 0.5.0 */ + .listAllNodeDevices = nodeListAllNodeDevices, /* 0.10.0 */ .deviceLookupByName = nodeDeviceLookupByName, /* 0.5.0 */ .deviceGetXMLDesc = nodeDeviceGetXMLDesc, /* 0.5.0 */ .deviceGetParent = nodeDeviceGetParent, /* 0.5.0 */ diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index 265cbd4..799768b 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -1757,6 +1757,7 @@ static virDeviceMonitor udevDeviceMonitor = { .close = udevNodeDrvClose, /* 0.7.3 */ .numOfDevices = nodeNumOfDevices, /* 0.7.3 */ .listDevices = nodeListDevices, /* 0.7.3 */ + .listAllNodeDevices = nodeListAllNodeDevices, /* 0.10.0 */ .deviceLookupByName = nodeDeviceLookupByName, /* 0.7.3 */ .deviceGetXMLDesc = nodeDeviceGetXMLDesc, /* 0.7.3 */ .deviceGetParent = nodeDeviceGetParent, /* 0.7.3 */ -- 1.7.7.3

The implementation is done manually as the generator does not support wrapping lists of C pointers into Python objects. python/libvirt-override-api.xml: Document python/libvirt-override-virConnect.py: * Implementation for listAllNodeDevices. python/libvirt-override.c: Implementation for the wrapper. --- python/libvirt-override-api.xml | 6 ++++ python/libvirt-override-virConnect.py | 12 ++++++++ python/libvirt-override.c | 48 +++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 0 deletions(-) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index ab6f407..a3d6bbb 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -346,6 +346,12 @@ <arg name='flags' type='unsigned int' info='flags (unused; pass 0)'/> <return type='str *' info='the list of Names or None in case of error'/> </function> + <function name='virConnectListAllNodeDevices' file='python'> + <info>returns list of all host node devices</info> + <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> + <arg name='flags' type='unsigned int' info='optional flags'/> + <return type='device *' info='the list of host node device or None in case of error'/> + </function> <function name='virNodeDeviceListCaps' file='python'> <info>list the node device's capabilities</info> <arg name='dev' type='virNodeDevicePtr' info='pointer to the node device'/> diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index ffa1a3c..0859c36 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -242,3 +242,15 @@ retlist.append(virInterface(self, _obj=ifaceptr)) return retlist + + def listAllDevices(self, flags): + """Returns a list of host node device objects""" + ret = libvirtmod.virConnectListAllNodeDevices(self._o, flags) + if ret is None: + raise libvirtError("virConnectListAllNodeDevices() failed", conn=self) + + retlist = list() + for devptr in ret: + retlist.append(virNodeDevice(self, _obj=devptr)) + + return retlist diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 98ed40f..6449639 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3383,6 +3383,53 @@ libvirt_virNodeListDevices(PyObject *self ATTRIBUTE_UNUSED, } static PyObject * +libvirt_virConnectListAllNodeDevices(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *pyobj_conn; + PyObject *py_retval = NULL; + PyObject *tmp = NULL; + virConnectPtr conn; + virNodeDevicePtr *devices = NULL; + int c_retval = 0; + int i; + unsigned int flags; + + if (!PyArg_ParseTuple(args, (char *)"Oi:virConnectListAllNodeDevices", + &pyobj_conn, &flags)) + return NULL; + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virConnectListAllNodeDevices(conn, &devices, flags); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + + if (!(py_retval = PyList_New(c_retval))) + goto cleanup; + + for (i = 0; i < c_retval; i++) { + if (!(tmp = libvirt_virNodeDevicePtrWrap(devices[i])) || + PyList_SetItem(py_retval, i, tmp) < 0) { + Py_XDECREF(tmp); + Py_DECREF(py_retval); + py_retval = NULL; + goto cleanup; + } + /* python steals the pointer */ + devices[i] = NULL; + } + +cleanup: + for (i = 0; i < c_retval; i++) + if (devices[i]) + virNodeDeviceFree(devices[i]); + VIR_FREE(devices); + return py_retval; +} + +static PyObject * libvirt_virNodeDeviceListCaps(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { PyObject *py_retval; @@ -6079,6 +6126,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virEventInvokeHandleCallback", libvirt_virEventInvokeHandleCallback, METH_VARARGS, NULL}, {(char *) "virEventInvokeTimeoutCallback", libvirt_virEventInvokeTimeoutCallback, METH_VARARGS, NULL}, {(char *) "virNodeListDevices", libvirt_virNodeListDevices, METH_VARARGS, NULL}, + {(char *) "virConnectListAllNodeDevices", libvirt_virConnectListAllNodeDevices, METH_VARARGS, NULL}, {(char *) "virNodeDeviceListCaps", libvirt_virNodeDeviceListCaps, METH_VARARGS, NULL}, {(char *) "virSecretGetUUID", libvirt_virSecretGetUUID, METH_VARARGS, NULL}, {(char *) "virSecretGetUUIDString", libvirt_virSecretGetUUIDString, METH_VARARGS, NULL}, -- 1.7.7.3

output of commands like '%virsh nodedev-list --tree --cap pci' could be empty. Remove the useless checking. --- tools/virsh-nodedev.c | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/virsh-nodedev.c b/tools/virsh-nodedev.c index 5a0987d..6848925 100644 --- a/tools/virsh-nodedev.c +++ b/tools/virsh-nodedev.c @@ -193,9 +193,8 @@ cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) virNodeDeviceFree(dev); } for (i = 0 ; i < num_devices ; i++) { - if (parents[i] == NULL && - vshTreePrint(ctl, vshNodeListLookup, &arrays, num_devices, - i) < 0) + if (vshTreePrint(ctl, vshNodeListLookup, + &arrays, num_devices, i) < 0) ret = false; } for (i = 0 ; i < num_devices ; i++) { -- 1.7.7.3

tools/virsh-nodedev.c: * vshNodeDeviceSorter to sort node devices by name * vshNodeDeviceListFree to free the node device objects list. * vshNodeDeviceListCollect to collect the node device objects, trying to use new API first, fall back to older APIs if it's not supported. * Change option --cap to accept multiple capability types. tools/virsh.pod * Update document for --cap --- tools/virsh-nodedev.c | 301 +++++++++++++++++++++++++++++++++++++++++++------ tools/virsh.pod | 8 +- 2 files changed, 269 insertions(+), 40 deletions(-) diff --git a/tools/virsh-nodedev.c b/tools/virsh-nodedev.c index 6848925..a226922 100644 --- a/tools/virsh-nodedev.c +++ b/tools/virsh-nodedev.c @@ -23,6 +23,8 @@ * */ +#include "conf/node_device_conf.h" + /* * "nodedev-create" command */ @@ -131,6 +133,186 @@ vshNodeListLookup(int devid, bool parent, void *opaque) return arrays->names[devid]; } +static int +vshNodeDeviceSorter(const void *a, const void *b) +{ + virNodeDevicePtr *na = (virNodeDevicePtr *) a; + virNodeDevicePtr *nb = (virNodeDevicePtr *) b; + + if (*na && !*nb) + return -1; + + if (!*na) + return *nb != NULL; + + return vshStrcasecmp(virNodeDeviceGetName(*na), + virNodeDeviceGetName(*nb)); +} + +struct vshNodeDeviceList { + virNodeDevicePtr *devices; + size_t ndevices; +}; +typedef struct vshNodeDeviceList *vshNodeDeviceListPtr; + +static void +vshNodeDeviceListFree(vshNodeDeviceListPtr list) +{ + int i; + + if (list && list->ndevices) { + for (i = 0; i < list->ndevices; i++) { + if (list->devices[i]) + virNodeDeviceFree(list->devices[i]); + } + VIR_FREE(list->devices); + } + VIR_FREE(list); +} + +static vshNodeDeviceListPtr +vshNodeDeviceListCollect(vshControl *ctl, + char **capnames, + int ncapnames, + unsigned int flags) +{ + vshNodeDeviceListPtr list = vshMalloc(ctl, sizeof(*list)); + int i; + int ret; + virNodeDevicePtr device; + bool success = false; + size_t deleted = 0; + int ndevices = 0; + char **names = NULL; + + /* try the list with flags support (0.10.0 and later) */ + if ((ret = virConnectListAllNodeDevices(ctl->conn, + &list->devices, + flags)) >= 0) { + list->ndevices = ret; + goto finished; + } + + /* check if the command is actually supported */ + if (last_error && last_error->code == VIR_ERR_NO_SUPPORT) { + vshResetLibvirtError(); + goto fallback; + } + + /* there was an error during the call */ + vshError(ctl, "%s", _("Failed to list node devices")); + goto cleanup; + + +fallback: + /* fall back to old method (0.9.13 and older) */ + vshResetLibvirtError(); + + ndevices = virNodeNumOfDevices(ctl->conn, NULL, 0); + if (ndevices < 0) { + vshError(ctl, "%s", _("Failed to count node devices")); + goto cleanup; + } + + if (ndevices == 0) + return list; + + names = vshMalloc(ctl, sizeof(char *) * ndevices); + + ndevices = virNodeListDevices(ctl->conn, NULL, names, ndevices, 0); + if (ndevices < 0) { + vshError(ctl, "%s", _("Failed to list node devices")); + goto cleanup; + } + + list->devices = vshMalloc(ctl, sizeof(virNodeDevicePtr) * (ndevices)); + list->ndevices = 0; + + /* get the node devices */ + for (i = 0; i < ndevices ; i++) { + if (!(device = virNodeDeviceLookupByName(ctl->conn, names[i]))) + continue; + list->devices[list->ndevices++] = device; + } + + /* truncate domains that weren't found */ + deleted = ndevices - list->ndevices; + + if (!capnames) + goto finished; + + /* filter the list if the list was acquired by fallback means */ + for (i = 0; i < list->ndevices; i++) { + char **caps = NULL; + int ncaps = 0; + bool match = false; + + device = list->devices[i]; + + if ((ncaps = virNodeDeviceNumOfCaps(device)) < 0) { + vshError(ctl, "%s", _("Failed to get capability numbers " + "of the device")); + goto cleanup; + } + + caps = vshMalloc(ctl, sizeof(char *) * ncaps); + + if ((ncaps = virNodeDeviceListCaps(device, caps, ncaps)) < 0) { + vshError(ctl, "%s", _("Failed to get capability names of the device")); + VIR_FREE(caps); + goto cleanup; + } + + int j, k; + for (j = 0; j < ncaps; j++) { + for (k = 0; k < ncapnames; k++) { + if (STREQ(caps[j], capnames[k])) { + match = true; + break; + } + } + } + + VIR_FREE(caps); + + if (!match) + goto remove_entry; + + /* the device matched all filters, it may stay */ + continue; + +remove_entry: + /* the device has to be removed as it failed one of the filters */ + virNodeDeviceFree(list->devices[i]); + list->devices[i] = NULL; + deleted++; + } + +finished: + /* sort the list */ + if (list->devices && list->ndevices) + qsort(list->devices, list->ndevices, + sizeof(*list->devices), vshNodeDeviceSorter); + + /* truncate the list if filter simulation deleted entries */ + if (deleted) + VIR_SHRINK_N(list->devices, list->ndevices, deleted); + + success = true; + +cleanup: + for (i = 0; i < ndevices; i++) + VIR_FREE(names[i]); + VIR_FREE(names); + + if (!success) { + vshNodeDeviceListFree(list); + list = NULL; + } + + return list; +} + /* * "nodedev-list" command */ @@ -142,73 +324,118 @@ static const vshCmdInfo info_node_list_devices[] = { static const vshCmdOptDef opts_node_list_devices[] = { {"tree", VSH_OT_BOOL, 0, N_("list devices in a tree")}, - {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, N_("capability name")}, + {"cap", VSH_OT_STRING, VSH_OFLAG_NONE, N_("capability names, separated by comma")}, {NULL, 0, 0, NULL} }; static bool cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { - const char *cap = NULL; - char **devices; - int num_devices, i; + const char *cap_str = NULL; + int i; bool tree = vshCommandOptBool(cmd, "tree"); bool ret = true; + unsigned int flags = 0; + char **caps = NULL; + int ncaps = 0; + vshNodeDeviceListPtr list = NULL; + int cap_type = -1; if (!vshConnectionUsability(ctl, ctl->conn)) return false; - if (vshCommandOptString(cmd, "cap", &cap) <= 0) - cap = NULL; + ignore_value(vshCommandOptString(cmd, "cap", &cap_str)); - num_devices = virNodeNumOfDevices(ctl->conn, cap, 0); - if (num_devices < 0) { - vshError(ctl, "%s", _("Failed to count node devices")); - return false; - } else if (num_devices == 0) { - return true; + if (cap_str) { + ncaps = vshStringToArray((char *)cap_str, &caps); + + for (i = 0; i < ncaps; i++) + for (i = 0; i < ncaps; i++) { + if ((cap_type = virNodeDevCapTypeFromString(caps[i])) < 0) { + vshError(ctl, "%s", _("Invalid capability type")); + VIR_FREE(caps); + return false; + } + + switch(cap_type) { + case VIR_NODE_DEV_CAP_SYSTEM: + flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_SYSTEM; + break; + case VIR_NODE_DEV_CAP_PCI_DEV: + flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_PCI_DEV; + break; + case VIR_NODE_DEV_CAP_USB_DEV: + flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_USB_DEV; + break; + case VIR_NODE_DEV_CAP_USB_INTERFACE: + flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_USB_INTERFACE; + break; + case VIR_NODE_DEV_CAP_NET: + flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_NET; + break; + case VIR_NODE_DEV_CAP_SCSI_HOST: + flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_HOST; + break; + case VIR_NODE_DEV_CAP_SCSI_TARGET: + flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_TARGET; + break; + case VIR_NODE_DEV_CAP_SCSI: + flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI; + break; + case VIR_NODE_DEV_CAP_STORAGE: + flags |= VIR_CONNECT_LIST_NODE_DEVICES_CAP_STORAGE; + break; + default: + break; + } + } } - devices = vshMalloc(ctl, sizeof(char *) * num_devices); - num_devices = - virNodeListDevices(ctl->conn, cap, devices, num_devices, 0); - if (num_devices < 0) { - vshError(ctl, "%s", _("Failed to list node devices")); - VIR_FREE(devices); - return false; + if (!(list = vshNodeDeviceListCollect(ctl, caps, ncaps, flags))) { + ret = false; + goto cleanup; } - qsort(&devices[0], num_devices, sizeof(char*), vshNameSorter); + if (tree) { - char **parents = vshMalloc(ctl, sizeof(char *) * num_devices); - struct vshNodeList arrays = { devices, parents }; + char **parents = vshMalloc(ctl, sizeof(char *) * list->ndevices); + char **names = vshMalloc(ctl, sizeof(char *) * list->ndevices); + struct vshNodeList arrays = { names, parents }; + + for (i = 0; i < list->ndevices; i++) + names[i] = vshStrdup(ctl, virNodeDeviceGetName(list->devices[i])); - for (i = 0; i < num_devices; i++) { - virNodeDevicePtr dev = virNodeDeviceLookupByName(ctl->conn, devices[i]); - if (dev && STRNEQ(devices[i], "computer")) { + for (i = 0; i < list->ndevices; i++) { + virNodeDevicePtr dev = list->devices[i]; + if (STRNEQ(names[i], "computer")) { const char *parent = virNodeDeviceGetParent(dev); parents[i] = parent ? vshStrdup(ctl, parent) : NULL; } else { parents[i] = NULL; } - virNodeDeviceFree(dev); } - for (i = 0 ; i < num_devices ; i++) { - if (vshTreePrint(ctl, vshNodeListLookup, - &arrays, num_devices, i) < 0) + + for (i = 0 ; i < list->ndevices; i++) { + if (vshTreePrint(ctl, vshNodeListLookup, &arrays, + list->ndevices, i) < 0) ret = false; } - for (i = 0 ; i < num_devices ; i++) { - VIR_FREE(devices[i]); + + for (i = 0 ; i < list->ndevices; i++) VIR_FREE(parents[i]); - } VIR_FREE(parents); + + for (i = 0; i < list->ndevices; i++) + VIR_FREE(names[i]); + VIR_FREE(names); } else { - for (i = 0; i < num_devices; i++) { - vshPrint(ctl, "%s\n", devices[i]); - VIR_FREE(devices[i]); - } + for (i = 0; i < list->ndevices; i++) + vshPrint(ctl, "%s\n", virNodeDeviceGetName(list->devices[i])); } - VIR_FREE(devices); + +cleanup: + VIR_FREE(cap_str); + VIR_FREE(caps); + vshNodeDeviceListFree(list); return ret; } diff --git a/tools/virsh.pod b/tools/virsh.pod index 4462efa..0ba297a 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1831,9 +1831,11 @@ libvirt (such as whether device reset is supported). =item B<nodedev-list> I<cap> I<--tree> List all of the devices available on the node that are known by libvirt. -If I<cap> is used, the list is filtered to show only the nodes that -include the given capability. If I<--tree> is used, the output is -formatted in a tree representing parents of each node. +I<cap> is used to filter the list by capability types, the types must be +separated by comma, e.g. --cap pci,scsi, valid capability types include +'system', 'pci', 'usb_device', 'usb', 'net', 'scsi_host', 'scsi_target', +'scsi', 'storage'. If I<--tree> is used, the output is formatted in a tree +representing parents of each node. =item B<nodedev-reattach> I<nodedev> -- 1.7.7.3

This is to list the network fitler objects. No flags are supported include/libvirt/libvirt.h.in: Declare enum virConnectListAllNWFilterFlags and virConnectListAllNWFilters. python/generator.py: Skip auto-generating src/driver.h: (virDrvConnectListAllNWFilters) src/libvirt.c: Implement the public API src/libvirt_public.syms: Export the symbol to public --- include/libvirt/libvirt.h.in | 4 ++- python/generator.py | 1 + src/driver.h | 5 ++++ src/libvirt.c | 50 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 5 files changed, 60 insertions(+), 1 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index fcade4d..2b542b8 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -4085,7 +4085,9 @@ int virConnectNumOfNWFilters (virConnectPtr conn); int virConnectListNWFilters (virConnectPtr conn, char **const names, int maxnames); - +int virConnectListAllNWFilters(virConnectPtr conn, + virNWFilterPtr **filters, + unsigned int flags); /* * Lookup nwfilter by name or uuid */ diff --git a/python/generator.py b/python/generator.py index f63730b..af657af 100755 --- a/python/generator.py +++ b/python/generator.py @@ -463,6 +463,7 @@ skip_function = ( 'virConnectListAllNetworks', # overridden in virConnect.py 'virConnectListAllInterfaces', # overridden in virConnect.py 'virConnectListAllNodeDevices', # overridden in virConnect.py + 'virConnectListAllNWFilters', # overridden in virConnect.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 3cfd499..933d994 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1618,6 +1618,10 @@ typedef int (*virDrvConnectListNWFilters) (virConnectPtr conn, char **const names, int maxnames); +typedef int + (*virDrvConnectListAllNWFilters) (virConnectPtr conn, + virNWFilterPtr **filters, + unsigned int flags); typedef virNWFilterPtr (*virDrvNWFilterLookupByName) (virConnectPtr conn, const char *name); @@ -1655,6 +1659,7 @@ struct _virNWFilterDriver { virDrvConnectNumOfNWFilters numOfNWFilters; virDrvConnectListNWFilters listNWFilters; + virDrvConnectListAllNWFilters listAllNWFilters; virDrvNWFilterLookupByName nwfilterLookupByName; virDrvNWFilterLookupByUUID nwfilterLookupByUUID; virDrvNWFilterDefineXML defineXML; diff --git a/src/libvirt.c b/src/libvirt.c index 9fa76f3..2f90202 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -15955,6 +15955,56 @@ error: return -1; } +/** + * virConnectListAllNWFilters: + * @conn: Pointer to the hypervisor connection. + * @filters: Pointer to a variable to store the array containing the network + * filter objects or NULL if the list is not required (just returns + * number of network filters). + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Collect the list of network filters, and allocate an array to store those + * objects. + * + * Returns the number of network filters found or -1 and sets @filters to NULL + * in case of error. On success, the array stored into @filters 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 + * virNWFilterFree() on each array element, then calling free() on @filters. + */ +int +virConnectListAllNWFilters(virConnectPtr conn, + virNWFilterPtr **filters, + unsigned int flags) +{ + VIR_DEBUG("conn=%p, filters=%p, flags=%x", conn, filters, flags); + + virResetLastError(); + + if (filters) + *filters = NULL; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->nwfilterDriver && + conn->nwfilterDriver->listAllNWFilters) { + int ret; + ret = conn->nwfilterDriver->listAllNWFilters(conn, filters, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} /** * virConnectListNWFilters: diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 05b32ee..4ef311f 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -554,6 +554,7 @@ LIBVIRT_0.10.0 { virConnectListAllNetworks; virConnectListAllInterfaces; virConnectListAllNodeDevices; + virConnectListAllNWFilters; } LIBVIRT_0.9.13; # .... define new API here using predicted next version number .... -- 1.7.7.3

The RPC generator doesn't support returning list of object yet, this patch do the work manually. * daemon/remote.c: Implemente the server side handler remoteDispatchConnectListAllNWFilters. * src/remote/remote_driver.c: Add remote driver handler remoteConnectListAllNWFilters. * src/remote/remote_protocol.x: New RPC procedure REMOTE_PROC_CONNECT_LIST_ALL_NWFILTERS and structs to represent the args and ret for it. * src/remote_protocol-structs: Likewise. --- daemon/remote.c | 54 ++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 63 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 13 ++++++++- src/remote_protocol-structs | 12 ++++++++ 4 files changed, 141 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 17ddc2e..0cf3829 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4219,6 +4219,60 @@ cleanup: return rv; } +static int +remoteDispatchConnectListAllNWFilters(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_connect_list_all_nwfilters_args *args, + remote_connect_list_all_nwfilters_ret *ret) +{ + virNWFilterPtr *filters = NULL; + int nfilters = 0; + int i; + int rv = -1; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if ((nfilters = virConnectListAllNWFilters(priv->conn, + args->need_results ? &filters : NULL, + args->flags)) < 0) + goto cleanup; + + if (filters && nfilters) { + if (VIR_ALLOC_N(ret->filters.filters_val, nfilters) < 0) { + virReportOOMError(); + goto cleanup; + } + + ret->filters.filters_len = nfilters; + + for (i = 0; i < nfilters; i++) + make_nonnull_nwfilter(ret->filters.filters_val + i, filters[i]); + } else { + ret->filters.filters_len = 0; + ret->filters.filters_val = NULL; + } + + ret->ret = nfilters; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (filters) { + for (i = 0; i < nfilters; i++) + virNWFilterFree(filters[i]); + VIR_FREE(filters); + } + return rv; +} + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index a0bdb32..cadd9b1 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -2989,6 +2989,68 @@ done: return rv; } +static int +remoteConnectListAllNWFilters(virConnectPtr conn, + virNWFilterPtr **filters, + unsigned int flags) +{ + int rv = -1; + int i; + virNWFilterPtr *tmp_filters = NULL; + remote_connect_list_all_nwfilters_args args; + remote_connect_list_all_nwfilters_ret ret; + + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + args.need_results = !!filters; + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + if (call(conn, + priv, + 0, + REMOTE_PROC_CONNECT_LIST_ALL_NWFILTERS, + (xdrproc_t) xdr_remote_connect_list_all_nwfilters_args, + (char *) &args, + (xdrproc_t) xdr_remote_connect_list_all_nwfilters_ret, + (char *) &ret) == -1) + goto done; + + if (filters) { + if (VIR_ALLOC_N(tmp_filters, ret.filters.filters_len + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < ret.filters.filters_len; i++) { + tmp_filters[i] = get_nonnull_nwfilter (conn, ret.filters.filters_val[i]); + if (!tmp_filters[i]) { + virReportOOMError(); + goto cleanup; + } + } + *filters = tmp_filters; + tmp_filters = NULL; + } + + rv = ret.ret; + +cleanup: + if (tmp_filters) { + for (i = 0; i < ret.filters.filters_len; i++) + if (tmp_filters[i]) + virNWFilterFree(tmp_filters[i]); + VIR_FREE(tmp_filters); + } + + xdr_free((xdrproc_t) xdr_remote_connect_list_all_nwfilters_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return rv; +} /*----------------------------------------------------------------------*/ @@ -5824,6 +5886,7 @@ static virNWFilterDriver nwfilter_driver = { .undefine = remoteNWFilterUndefine, /* 0.8.0 */ .numOfNWFilters = remoteNumOfNWFilters, /* 0.8.0 */ .listNWFilters = remoteListNWFilters, /* 0.8.0 */ + .listAllNWFilters = remoteConnectListAllNWFilters, /* 0.10.0 */ }; diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 4a7116c..990e272 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2578,6 +2578,16 @@ struct remote_connect_list_all_node_devices_ret { unsigned int ret; }; +struct remote_connect_list_all_nwfilters_args { + int need_results; + unsigned int flags; +}; + +struct remote_connect_list_all_nwfilters_ret { + remote_nonnull_nwfilter filters<>; + unsigned int ret; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -2910,7 +2920,8 @@ enum remote_procedure { REMOTE_PROC_CONNECT_LIST_ALL_NETWORKS = 280, /* skipgen skipgen priority:high */ REMOTE_PROC_CONNECT_LIST_ALL_INTERFACES = 281, /* skipgen skipgen priority:high */ - REMOTE_PROC_CONNECT_LIST_ALL_NODE_DEVICES = 282 /* skipgen skipgen priority:high */ + REMOTE_PROC_CONNECT_LIST_ALL_NODE_DEVICES = 282, /* skipgen skipgen priority:high */ + REMOTE_PROC_CONNECT_LIST_ALL_NWFILTERS = 283 /* skipgen skipgen priority:high */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index aae8652..34cc5ba 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2037,6 +2037,17 @@ struct remote_connect_list_all_node_devices_ret { } devices; u_int ret; }; +struct remote_connect_list_all_nwfilters_args { + int need_results; + u_int flags; +}; +struct remote_connect_list_all_nwfilters_ret { + struct { + u_int filters_len; + remote_nonnull_nwfilter * filters_val; + } filters; + u_int ret; +}; enum remote_procedure { REMOTE_PROC_OPEN = 1, REMOTE_PROC_CLOSE = 2, @@ -2320,4 +2331,5 @@ enum remote_procedure { REMOTE_PROC_CONNECT_LIST_ALL_NETWORKS = 280, REMOTE_PROC_CONNECT_LIST_ALL_INTERFACES = 281, REMOTE_PROC_CONNECT_LIST_ALL_NODE_DEVICES = 282, + REMOTE_PROC_CONNECT_LIST_ALL_NWFILTERS = 283, }; -- 1.7.7.3

Simply returns the object list. No filtering. src/nwfilter/nwfilter_driver.c: Implement listAllNWFilters --- src/nwfilter/nwfilter_driver.c | 56 ++++++++++++++++++++++++++++++++++++++++ 1 files changed, 56 insertions(+), 0 deletions(-) diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c index 4fa73f8..9b0cf2c 100644 --- a/src/nwfilter/nwfilter_driver.c +++ b/src/nwfilter/nwfilter_driver.c @@ -338,6 +338,61 @@ nwfilterListNWFilters(virConnectPtr conn, } +static int +nwfilterListAllNWFilters(virConnectPtr conn, + virNWFilterPtr **filters, + unsigned int flags) { + virNWFilterDriverStatePtr driver = conn->nwfilterPrivateData; + virNWFilterPtr *tmp_filters = NULL; + int nfilters = 0; + virNWFilterPtr filter = NULL; + virNWFilterObjPtr obj = NULL; + int i; + int ret = -1; + + virCheckFlags(0, -1); + + nwfilterDriverLock(driver); + + if (!filters) { + ret = driver->nwfilters.count; + goto cleanup; + } + + if (VIR_ALLOC_N(tmp_filters, driver->nwfilters.count + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0 ; i < driver->nwfilters.count; i++) { + obj = driver->nwfilters.objs[i]; + virNWFilterObjLock(obj); + if (!(filter = virGetNWFilter(conn, obj->def->name, + obj->def->uuid))) { + virNWFilterObjUnlock(obj); + goto cleanup; + } + virNWFilterObjUnlock(obj); + tmp_filters[nfilters++] = filter; + } + + *filters = tmp_filters; + tmp_filters = NULL; + ret = nfilters; + + cleanup: + nwfilterDriverUnlock(driver); + if (tmp_filters) { + for (i = 0; i < nfilters; i ++) { + if (tmp_filters[i]) + virNWFilterFree(tmp_filters[i]); + } + } + VIR_FREE(tmp_filters); + + return ret; +} + static virNWFilterPtr nwfilterDefine(virConnectPtr conn, const char *xml) @@ -473,6 +528,7 @@ static virNWFilterDriver nwfilterDriver = { .close = nwfilterClose, /* 0.8.0 */ .numOfNWFilters = nwfilterNumNWFilters, /* 0.8.0 */ .listNWFilters = nwfilterListNWFilters, /* 0.8.0 */ + .listAllNWFilters = nwfilterListAllNWFilters, /* 0.10.0 */ .nwfilterLookupByName = nwfilterLookupByName, /* 0.8.0 */ .nwfilterLookupByUUID = nwfilterLookupByUUID, /* 0.8.0 */ .defineXML = nwfilterDefine, /* 0.8.0 */ -- 1.7.7.3

tools/virsh-nwfilter.c: * vshNWFilterSorter to sort network filters by name * vshNWFilterListFree to free the network filter objects list. * vshNWFilterListCollect to collect the network filter objects, trying to use new API first, fall back to older APIs if it's not supported. --- tools/virsh-nwfilter.c | 163 +++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 134 insertions(+), 29 deletions(-) diff --git a/tools/virsh-nwfilter.c b/tools/virsh-nwfilter.c index e937b63..5d39874 100644 --- a/tools/virsh-nwfilter.c +++ b/tools/virsh-nwfilter.c @@ -188,6 +188,134 @@ cmdNWFilterDumpXML(vshControl *ctl, const vshCmd *cmd) return ret; } +static int +vshNWFilterSorter(const void *a, const void *b) +{ + virNWFilterPtr *fa = (virNWFilterPtr *) a; + virNWFilterPtr *fb = (virNWFilterPtr *) b; + + if (*fa && !*fb) + return -1; + + if (!*fa) + return *fb != NULL; + + return vshStrcasecmp(virNWFilterGetName(*fa), + virNWFilterGetName(*fb)); +} + +struct vshNWFilterList { + virNWFilterPtr *filters; + size_t nfilters; +}; +typedef struct vshNWFilterList *vshNWFilterListPtr; + +static void +vshNWFilterListFree(vshNWFilterListPtr list) +{ + int i; + + if (list && list->nfilters) { + for (i = 0; i < list->nfilters; i++) { + if (list->filters[i]) + virNWFilterFree(list->filters[i]); + } + VIR_FREE(list->filters); + } + VIR_FREE(list); +} + +static vshNWFilterListPtr +vshNWFilterListCollect(vshControl *ctl, + unsigned int flags) +{ + vshNWFilterListPtr list = vshMalloc(ctl, sizeof(*list)); + int i; + int ret; + virNWFilterPtr filter; + bool success = false; + size_t deleted = 0; + int nfilters = 0; + char **names = NULL; + + /* try the list with flags support (0.10.0 and later) */ + if ((ret = virConnectListAllNWFilters(ctl->conn, + &list->filters, + flags)) >= 0) { + list->nfilters = ret; + goto finished; + } + + /* check if the command is actually supported */ + if (last_error && last_error->code == VIR_ERR_NO_SUPPORT) { + vshResetLibvirtError(); + goto fallback; + } + + /* there was an error during the call */ + vshError(ctl, "%s", _("Failed to list node filters")); + goto cleanup; + + +fallback: + /* fall back to old method (0.9.13 and older) */ + vshResetLibvirtError(); + + nfilters = virConnectNumOfNWFilters(ctl->conn); + if (nfilters < 0) { + vshError(ctl, "%s", _("Failed to count network filters")); + goto cleanup; + } + + if (nfilters == 0) + return list; + + names = vshMalloc(ctl, sizeof(char *) * nfilters); + + nfilters = virConnectListNWFilters(ctl->conn, names, nfilters); + if (nfilters < 0) { + vshError(ctl, "%s", _("Failed to list network filters")); + goto cleanup; + } + + list->filters = vshMalloc(ctl, sizeof(virNWFilterPtr) * (nfilters)); + list->nfilters = 0; + + /* get the network filters */ + for (i = 0; i < nfilters ; i++) { + if (!(filter = virNWFilterLookupByName(ctl->conn, names[i]))) + continue; + list->filters[list->nfilters++] = filter; + } + + /* truncate network filters that weren't found */ + deleted = nfilters - list->nfilters; + +finished: + /* sort the list */ + if (list->filters && list->nfilters) + qsort(list->filters, list->nfilters, + sizeof(*list->filters), vshNWFilterSorter); + + /* truncate the list for not found filter objects */ + if (deleted) + VIR_SHRINK_N(list->filters, list->nfilters, deleted); + + success = true; + +cleanup: + for (i = 0; i < nfilters; i++) + VIR_FREE(names[i]); + VIR_FREE(names); + + if (!success) { + vshNWFilterListFree(list); + list = NULL; + } + + return list; +} + /* * "nwfilter-list" command */ @@ -204,53 +332,30 @@ static const vshCmdOptDef opts_nwfilter_list[] = { static bool cmdNWFilterList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { - int numfilters, i; - char **names; + int i; char uuid[VIR_UUID_STRING_BUFLEN]; + vshNWFilterListPtr list = NULL; if (!vshConnectionUsability(ctl, ctl->conn)) return false; - numfilters = virConnectNumOfNWFilters(ctl->conn); - if (numfilters < 0) { - vshError(ctl, "%s", _("Failed to list network filters")); - return false; - } - - names = vshMalloc(ctl, sizeof(char *) * numfilters); - - if ((numfilters = virConnectListNWFilters(ctl->conn, names, - numfilters)) < 0) { - vshError(ctl, "%s", _("Failed to list network filters")); - VIR_FREE(names); + if (!(list = vshNWFilterListCollect(ctl, 0))) return false; - } - - qsort(&names[0], numfilters, sizeof(char *), vshNameSorter); vshPrintExtra(ctl, "%-36s %-20s \n", _("UUID"), _("Name")); vshPrintExtra(ctl, "----------------------------------------------------------------\n"); - for (i = 0; i < numfilters; i++) { - virNWFilterPtr nwfilter = - virNWFilterLookupByName(ctl->conn, names[i]); - - /* this kind of work with networks is not atomic operation */ - if (!nwfilter) { - VIR_FREE(names[i]); - continue; - } + for (i = 0; i < list->nfilters; i++) { + virNWFilterPtr nwfilter = list->filters[i]; virNWFilterGetUUIDString(nwfilter, uuid); vshPrint(ctl, "%-36s %-20s\n", uuid, virNWFilterGetName(nwfilter)); - virNWFilterFree(nwfilter); - VIR_FREE(names[i]); } - VIR_FREE(names); + vshNWFilterListFree(list); return true; } -- 1.7.7.3

The implementation is done manually as the generator does not support wrapping lists of C pointers into Python objects. python/libvirt-override-api.xml: Document python/libvirt-override-virConnect.py: * Implementation for listAllNWFilters. python/libvirt-override.c: Implementation for the wrapper. --- python/libvirt-override-api.xml | 6 ++++ python/libvirt-override-virConnect.py | 12 ++++++++ python/libvirt-override.c | 48 +++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 0 deletions(-) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index a3d6bbb..b8ab823 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -396,6 +396,12 @@ <arg name='conn' type='virConnectPtr' info='virConnect connection'/> <return type='str *' info='the list of network filter IDs or None in case of error'/> </function> + <function name='virConnectListAllNWFilters' file='python'> + <info>returns list of all network fitlers</info> + <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> + <arg name='flags' type='unsigned int' info='optional flags'/> + <return type='nwfilter *' info='the list of network filters or None in case of error'/> + </function> <function name='virNWFilterLookupByUUID' file='python'> <info>Try to lookup a network filter on the given hypervisor based on its UUID.</info> <return type='virNWFilterPtr' info='a new network filter object or NULL in case of failure'/> diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index 0859c36..caca982 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -254,3 +254,15 @@ retlist.append(virNodeDevice(self, _obj=devptr)) return retlist + + def listAllNWFilters(self, flags): + """Returns a list of network filter objects""" + ret = libvirtmod.virConnectListAllNWFilters(self._o, flags) + if ret is None: + raise libvirtError("virConnectListAllNWFilters() failed", conn=self) + + retlist = list() + for filter_ptr in ret: + retlist.append(virNWFilter(self, _obj=filter_ptr)) + + return retlist diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 6449639..bdf5d12 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3764,6 +3764,53 @@ libvirt_virConnectListNWFilters(PyObject *self ATTRIBUTE_UNUSED, } static PyObject * +libvirt_virConnectListAllNWFilters(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *pyobj_conn; + PyObject *py_retval = NULL; + PyObject *tmp = NULL; + virConnectPtr conn; + virNWFilterPtr *filters = NULL; + int c_retval = 0; + int i; + unsigned int flags; + + if (!PyArg_ParseTuple(args, (char *)"Oi:virConnectListAllNWFilters", + &pyobj_conn, &flags)) + return NULL; + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virConnectListAllNWFilters(conn, &filters, flags); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + + if (!(py_retval = PyList_New(c_retval))) + goto cleanup; + + for (i = 0; i < c_retval; i++) { + if (!(tmp = libvirt_virNWFilterPtrWrap(filters[i])) || + PyList_SetItem(py_retval, i, tmp) < 0) { + Py_XDECREF(tmp); + Py_DECREF(py_retval); + py_retval = NULL; + goto cleanup; + } + /* python steals the pointer */ + filters[i] = NULL; + } + +cleanup: + for (i = 0; i < c_retval; i++) + if (filters[i]) + virNWFilterFree(filters[i]); + VIR_FREE(filters); + return py_retval; +} + +static PyObject * libvirt_virConnectListInterfaces(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { PyObject *py_retval; @@ -6138,6 +6185,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virNWFilterGetUUIDString", libvirt_virNWFilterGetUUIDString, METH_VARARGS, NULL}, {(char *) "virNWFilterLookupByUUID", libvirt_virNWFilterLookupByUUID, METH_VARARGS, NULL}, {(char *) "virConnectListNWFilters", libvirt_virConnectListNWFilters, METH_VARARGS, NULL}, + {(char *) "virConnectListAllNWFilters", libvirt_virConnectListAllNWFilters, METH_VARARGS, NULL}, {(char *) "virConnectListInterfaces", libvirt_virConnectListInterfaces, METH_VARARGS, NULL}, {(char *) "virConnectListDefinedInterfaces", libvirt_virConnectListDefinedInterfaces, METH_VARARGS, NULL}, {(char *) "virConnectListAllInterfaces", libvirt_virConnectListAllInterfaces, METH_VARARGS, NULL}, -- 1.7.7.3

This is to list the secret objects. No flags are supported include/libvirt/libvirt.h.in: Declare enum virConnectListAllSecretFlags and virConnectListAllSecrets. python/generator.py: Skip auto-generating src/driver.h: (virDrvConnectListAllSecrets) src/libvirt.c: Implement the public API src/libvirt_public.syms: Export the symbol to public --- include/libvirt/libvirt.h.in | 3 ++ python/generator.py | 1 + src/driver.h | 5 ++++ src/libvirt.c | 50 ++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 5 files changed, 60 insertions(+), 0 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 2b542b8..3f306f3 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -3208,6 +3208,9 @@ int virConnectNumOfSecrets (virConnectPtr conn); int virConnectListSecrets (virConnectPtr conn, char **uuids, int maxuuids); +int virConnectListAllSecrets(virConnectPtr conn, + virSecretPtr **secrets, + unsigned int flags); virSecretPtr virSecretLookupByUUID(virConnectPtr conn, const unsigned char *uuid); virSecretPtr virSecretLookupByUUIDString(virConnectPtr conn, diff --git a/python/generator.py b/python/generator.py index af657af..baa6f1a 100755 --- a/python/generator.py +++ b/python/generator.py @@ -464,6 +464,7 @@ skip_function = ( 'virConnectListAllInterfaces', # overridden in virConnect.py 'virConnectListAllNodeDevices', # overridden in virConnect.py 'virConnectListAllNWFilters', # overridden in virConnect.py + 'virConnectListAllSecrets', # overridden in virConnect.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 933d994..fa4e33f 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1547,6 +1547,10 @@ typedef int (*virDrvListSecrets) (virConnectPtr conn, char **uuids, int maxuuids); +typedef int + (*virDrvListAllSecrets) (virConnectPtr conn, + virSecretPtr **secrets, + unsigned int flags); typedef struct _virSecretDriver virSecretDriver; typedef virSecretDriver *virSecretDriverPtr; @@ -1568,6 +1572,7 @@ struct _virSecretDriver { virDrvNumOfSecrets numOfSecrets; virDrvListSecrets listSecrets; + virDrvListAllSecrets listAllSecrets; virDrvSecretLookupByUUID lookupByUUID; virDrvSecretLookupByUsage lookupByUsage; virDrvSecretDefineXML defineXML; diff --git a/src/libvirt.c b/src/libvirt.c index 2f90202..9ad6279 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -14384,6 +14384,56 @@ error: } /** + * virConnectListAllSecrets: + * @conn: Pointer to the hypervisor connection. + * @secrets: Pointer to a variable to store the array containing the secret + * objects or NULL if the list is not required (just returns the + * number of secrets). + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Collect the list of secrets, and allocate an array to store those objects. + * + * Returns the number of secrets found or -1 and sets @secrets to NULL in case + * of error. On success, the array stored into @secrets 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 + * virSecretFree() on each array element, then calling free() on @secrets. + */ +int +virConnectListAllSecrets(virConnectPtr conn, + virSecretPtr **secrets, + unsigned int flags) +{ + VIR_DEBUG("conn=%p, secrets=%p, flags=%x", conn, secrets, flags); + + virResetLastError(); + + if (secrets) + *secrets = NULL; + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + if (conn->secretDriver && + conn->secretDriver->listAllSecrets) { + int ret; + ret = conn->secretDriver->listAllSecrets(conn, secrets, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(conn); + return -1; +} + +/** * virConnectListSecrets: * @conn: virConnect connection * @uuids: Pointer to an array to store the UUIDs diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4ef311f..83b37b0 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -555,6 +555,7 @@ LIBVIRT_0.10.0 { virConnectListAllInterfaces; virConnectListAllNodeDevices; virConnectListAllNWFilters; + virConnectListAllSecrets; } LIBVIRT_0.9.13; # .... define new API here using predicted next version number .... -- 1.7.7.3

The RPC generator doesn't support returning list of object yet, this patch do the work manually. * daemon/remote.c: Implemente the server side handler remoteDispatchConnectListAllSecrets. * src/remote/remote_driver.c: Add remote driver handler remoteConnectListAllSecrets. * src/remote/remote_protocol.x: New RPC procedure REMOTE_PROC_CONNECT_LIST_ALL_SECRETS and structs to represent the args and ret for it. * src/remote_protocol-structs: Likewise. --- daemon/remote.c | 54 +++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 64 ++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 13 ++++++++- src/remote_protocol-structs | 12 ++++++++ 4 files changed, 142 insertions(+), 1 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 0cf3829..676f1cb 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4273,6 +4273,60 @@ cleanup: return rv; } +static int +remoteDispatchConnectListAllSecrets(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_connect_list_all_secrets_args *args, + remote_connect_list_all_secrets_ret *ret) +{ + virSecretPtr *secrets = NULL; + int nsecrets = 0; + int i; + int rv = -1; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + if ((nsecrets = virConnectListAllSecrets(priv->conn, + args->need_results ? &secrets : NULL, + args->flags)) < 0) + goto cleanup; + + if (secrets && nsecrets) { + if (VIR_ALLOC_N(ret->secrets.secrets_val, nsecrets) < 0) { + virReportOOMError(); + goto cleanup; + } + + ret->secrets.secrets_len = nsecrets; + + for (i = 0; i < nsecrets; i++) + make_nonnull_secret(ret->secrets.secrets_val + i, secrets[i]); + } else { + ret->secrets.secrets_len = 0; + ret->secrets.secrets_val = NULL; + } + + ret->ret = nsecrets; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (secrets) { + for (i = 0; i < nsecrets; i++) + virSecretFree(secrets[i]); + VIR_FREE(secrets); + } + return rv; +} + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index cadd9b1..4e68fcf 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -3052,6 +3052,69 @@ done: return rv; } +static int +remoteConnectListAllSecrets(virConnectPtr conn, + virSecretPtr **secrets, + unsigned int flags) +{ + int rv = -1; + int i; + virSecretPtr *tmp_secrets = NULL; + remote_connect_list_all_secrets_args args; + remote_connect_list_all_secrets_ret ret; + + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + args.need_results = !!secrets; + args.flags = flags; + + memset(&ret, 0, sizeof(ret)); + if (call(conn, + priv, + 0, + REMOTE_PROC_CONNECT_LIST_ALL_SECRETS, + (xdrproc_t) xdr_remote_connect_list_all_secrets_args, + (char *) &args, + (xdrproc_t) xdr_remote_connect_list_all_secrets_ret, + (char *) &ret) == -1) + goto done; + + if (secrets) { + if (VIR_ALLOC_N(tmp_secrets, ret.secrets.secrets_len + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < ret.secrets.secrets_len; i++) { + tmp_secrets[i] = get_nonnull_secret (conn, ret.secrets.secrets_val[i]); + if (!tmp_secrets[i]) { + virReportOOMError(); + goto cleanup; + } + } + *secrets = tmp_secrets; + tmp_secrets = NULL; + } + + rv = ret.ret; + +cleanup: + if (tmp_secrets) { + for (i = 0; i < ret.secrets.secrets_len; i++) + if (tmp_secrets[i]) + virSecretFree(tmp_secrets[i]); + VIR_FREE(tmp_secrets); + } + + xdr_free((xdrproc_t) xdr_remote_connect_list_all_secrets_ret, (char *) &ret); + +done: + remoteDriverUnlock(priv); + return rv; +} + /*----------------------------------------------------------------------*/ static virDrvOpenStatus ATTRIBUTE_NONNULL (1) @@ -5850,6 +5913,7 @@ static virSecretDriver secret_driver = { .close = remoteSecretClose, /* 0.7.1 */ .numOfSecrets = remoteNumOfSecrets, /* 0.7.1 */ .listSecrets = remoteListSecrets, /* 0.7.1 */ + .listAllSecrets = remoteConnectListAllSecrets, /* 0.10.0 */ .lookupByUUID = remoteSecretLookupByUUID, /* 0.7.1 */ .lookupByUsage = remoteSecretLookupByUsage, /* 0.7.1 */ .defineXML = remoteSecretDefineXML, /* 0.7.1 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 990e272..eb73fa2 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2588,6 +2588,16 @@ struct remote_connect_list_all_nwfilters_ret { unsigned int ret; }; +struct remote_connect_list_all_secrets_args { + int need_results; + unsigned int flags; +}; + +struct remote_connect_list_all_secrets_ret { + remote_nonnull_secret secrets<>; + unsigned int ret; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -2921,7 +2931,8 @@ enum remote_procedure { REMOTE_PROC_CONNECT_LIST_ALL_INTERFACES = 281, /* skipgen skipgen priority:high */ REMOTE_PROC_CONNECT_LIST_ALL_NODE_DEVICES = 282, /* skipgen skipgen priority:high */ - REMOTE_PROC_CONNECT_LIST_ALL_NWFILTERS = 283 /* skipgen skipgen priority:high */ + REMOTE_PROC_CONNECT_LIST_ALL_NWFILTERS = 283, /* skipgen skipgen priority:high */ + REMOTE_PROC_CONNECT_LIST_ALL_SECRETS = 284 /* skipgen skipgen priority:high */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 34cc5ba..1c8bc3c 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2048,6 +2048,17 @@ struct remote_connect_list_all_nwfilters_ret { } filters; u_int ret; }; +struct remote_list_all_secrets_args { + int need_results; + u_int flags; +}; +struct remote_list_all_secrets_ret { + struct { + u_int secrets_len; + remote_nonnull_network * secrets_val; + } secrets; + u_int ret; +}; enum remote_procedure { REMOTE_PROC_OPEN = 1, REMOTE_PROC_CLOSE = 2, @@ -2332,4 +2343,5 @@ enum remote_procedure { REMOTE_PROC_CONNECT_LIST_ALL_INTERFACES = 281, REMOTE_PROC_CONNECT_LIST_ALL_NODE_DEVICES = 282, REMOTE_PROC_CONNECT_LIST_ALL_NWFILTERS = 283, + REMOTE_PROC_CONNECT_LIST_ALL_SECRETS = 284, }; -- 1.7.7.3

Simply returns the object list. No filtering. src/secret/secret_driver.c: Implement listAllSecrets --- src/secret/secret_driver.c | 59 +++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 58 insertions(+), 1 deletions(-) diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index 7f92776..ed759ed 100644 --- a/src/secret/secret_driver.c +++ b/src/secret/secret_driver.c @@ -601,7 +601,6 @@ cleanup: return -1; } - static const char * secretUsageIDForDef(virSecretDefPtr def) { @@ -620,6 +619,63 @@ secretUsageIDForDef(virSecretDefPtr def) } } +static int +secretListAllSecrets(virConnectPtr conn, + virSecretPtr **secrets, + unsigned int flags) { + virSecretDriverStatePtr driver = conn->secretPrivateData; + virSecretPtr *tmp_secrets = NULL; + int nsecrets = 0; + int ret_nsecrets = 0; + virSecretPtr secret = NULL; + virSecretEntryPtr entry = NULL; + int i = 0; + int ret = -1; + + virCheckFlags(0, -1); + + secretDriverLock(driver); + + for (entry = driver->secrets; entry != NULL; entry = entry->next) + nsecrets++; + + if (!secrets) { + ret = nsecrets; + goto cleanup; + } + + if (VIR_ALLOC_N(tmp_secrets, nsecrets + 1) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (entry = driver->secrets; entry != NULL; entry = entry->next) { + if (!(secret = virGetSecret(conn, + entry->def->uuid, + entry->def->usage_type, + secretUsageIDForDef(entry->def)))) + goto cleanup; + tmp_secrets[ret_nsecrets++] = secret; + } + + *secrets = tmp_secrets; + tmp_secrets = NULL; + ret = ret_nsecrets; + + cleanup: + secretDriverUnlock(driver); + if (tmp_secrets) { + for (i = 0; i < ret_nsecrets; i ++) { + if (tmp_secrets[i]) + virSecretFree(tmp_secrets[i]); + } + } + VIR_FREE(tmp_secrets); + + return ret; +} + + static virSecretPtr secretLookupByUUID(virConnectPtr conn, const unsigned char *uuid) { @@ -1072,6 +1128,7 @@ static virSecretDriver secretDriver = { .close = secretClose, /* 0.7.1 */ .numOfSecrets = secretNumOfSecrets, /* 0.7.1 */ .listSecrets = secretListSecrets, /* 0.7.1 */ + .listAllSecrets = secretListAllSecrets, /* 0.10.0 */ .lookupByUUID = secretLookupByUUID, /* 0.7.1 */ .lookupByUsage = secretLookupByUsage, /* 0.7.1 */ .defineXML = secretDefineXML, /* 0.7.1 */ -- 1.7.7.3

tools/virsh.c: Remove vshNameSorter for nobody uses it tools/virsh-secret.c: * vshSecretSorter to sort secret object by name * vshSecretListFree to free the secret objects list. * vshSecretListCollect to collect the secret objects, trying to use new API first, fall back to older APIs if it's not supported. --- tools/virsh-secret.c | 182 ++++++++++++++++++++++++++++++++++++++++++-------- tools/virsh.c | 9 --- 2 files changed, 153 insertions(+), 38 deletions(-) diff --git a/tools/virsh-secret.c b/tools/virsh-secret.c index e6c2ece..4d87dda 100644 --- a/tools/virsh-secret.c +++ b/tools/virsh-secret.c @@ -290,6 +290,139 @@ cleanup: return ret; } +static int +vshSecretSorter(const void *a, const void *b) +{ + virSecretPtr *sa = (virSecretPtr *) a; + virSecretPtr *sb = (virSecretPtr *) b; + char uuid_sa[VIR_UUID_STRING_BUFLEN]; + char uuid_sb[VIR_UUID_STRING_BUFLEN]; + + if (*sa && !*sb) + return -1; + + if (!*sa) + return *sb != NULL; + + virSecretGetUUIDString(*sa, uuid_sa); + virSecretGetUUIDString(*sb, uuid_sb); + + return vshStrcasecmp(uuid_sa, uuid_sb); +} + +struct vshSecretList { + virSecretPtr *secrets; + size_t nsecrets; +}; +typedef struct vshSecretList *vshSecretListPtr; + +static void +vshSecretListFree(vshSecretListPtr list) +{ + int i; + + if (list && list->nsecrets) { + for (i = 0; i < list->nsecrets; i++) { + if (list->secrets[i]) + virSecretFree(list->secrets[i]); + } + VIR_FREE(list->secrets); + } + VIR_FREE(list); +} + +static vshSecretListPtr +vshSecretListCollect(vshControl *ctl, + unsigned int flags) +{ + vshSecretListPtr list = vshMalloc(ctl, sizeof(*list)); + int i; + int ret; + virSecretPtr secret; + bool success = false; + size_t deleted = 0; + int nsecrets = 0; + char **uuids = NULL; + + /* try the list with flags support (0.10.0 and later) */ + if ((ret = virConnectListAllSecrets(ctl->conn, + &list->secrets, + flags)) >= 0) { + list->nsecrets = ret; + goto finished; + } + + /* check if the command is actually supported */ + if (last_error && last_error->code == VIR_ERR_NO_SUPPORT) { + virFreeError(last_error); + last_error = NULL; + goto fallback; + } + + /* there was an error during the call */ + vshError(ctl, "%s", _("Failed to list node secrets")); + goto cleanup; + + +fallback: + /* fall back to old method (0.9.13 and older) */ + virResetLastError(); + + nsecrets = virConnectNumOfSecrets(ctl->conn); + if (nsecrets < 0) { + vshError(ctl, "%s", _("Failed to count secrets")); + goto cleanup; + } + + if (nsecrets == 0) + return list; + + uuids = vshMalloc(ctl, sizeof(char *) * nsecrets); + + nsecrets = virConnectListSecrets(ctl->conn, uuids, nsecrets); + if (nsecrets < 0) { + vshError(ctl, "%s", _("Failed to list secrets")); + goto cleanup; + } + + list->secrets = vshMalloc(ctl, sizeof(virSecretPtr) * (nsecrets)); + list->nsecrets = 0; + + /* get the secrets */ + for (i = 0; i < nsecrets ; i++) { + if (!(secret = virSecretLookupByUUIDString(ctl->conn, uuids[i]))) + continue; + list->secrets[list->nsecrets++] = secret; + } + + /* truncate secrets that weren't found */ + deleted = nsecrets - list->nsecrets; + +finished: + /* sort the list */ + if (list->secrets && list->nsecrets) + qsort(list->secrets, list->nsecrets, + sizeof(*list->secrets), vshSecretSorter); + + /* truncate the list for not found secret objects */ + if (deleted) + VIR_SHRINK_N(list->secrets, list->nsecrets, deleted); + + success = true; + +cleanup: + for (i = 0; i < nsecrets; i++) + VIR_FREE(uuids[i]); + VIR_FREE(uuids); + + if (!success) { + vshSecretListFree(list); + list = NULL; + } + + return list; +} + /* * "secret-list" command */ @@ -302,59 +435,50 @@ static const vshCmdInfo info_secret_list[] = { static bool cmdSecretList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) { - int maxuuids = 0, i; - char **uuids = NULL; + int i; + vshSecretListPtr list = NULL; + bool ret = false; if (!vshConnectionUsability(ctl, ctl->conn)) return false; - maxuuids = virConnectNumOfSecrets(ctl->conn); - if (maxuuids < 0) { - vshError(ctl, "%s", _("Failed to list secrets")); - return false; - } - uuids = vshMalloc(ctl, sizeof(*uuids) * maxuuids); - - maxuuids = virConnectListSecrets(ctl->conn, uuids, maxuuids); - if (maxuuids < 0) { - vshError(ctl, "%s", _("Failed to list secrets")); - VIR_FREE(uuids); + if (!(list = vshSecretListCollect(ctl, 0))) return false; - } - - qsort(uuids, maxuuids, sizeof(char *), vshNameSorter); vshPrintExtra(ctl, "%-36s %s\n", _("UUID"), _("Usage")); vshPrintExtra(ctl, "-----------------------------------------------------------\n"); - for (i = 0; i < maxuuids; i++) { - virSecretPtr sec = virSecretLookupByUUIDString(ctl->conn, uuids[i]); + for (i = 0; i < list->nsecrets; i++) { + virSecretPtr sec = list->secrets[i]; const char *usageType = NULL; - if (!sec) { - VIR_FREE(uuids[i]); - continue; - } - switch (virSecretGetUsageType(sec)) { case VIR_SECRET_USAGE_TYPE_VOLUME: usageType = _("Volume"); break; } + char uuid[VIR_UUID_STRING_BUFLEN]; + if (virSecretGetUUIDString(list->secrets[i], uuid) < 0) { + vshError(ctl, "%s", _("Failed to get uuid of secret")); + goto cleanup; + } + if (usageType) { vshPrint(ctl, "%-36s %s %s\n", - uuids[i], usageType, + uuid, usageType, virSecretGetUsageID(sec)); } else { vshPrint(ctl, "%-36s %s\n", - uuids[i], _("Unused")); + uuid, _("Unused")); } - virSecretFree(sec); - VIR_FREE(uuids[i]); } - VIR_FREE(uuids); - return true; + + ret = true; + +cleanup: + vshSecretListFree(list); + return ret; } static const vshCmdDef secretCmds[] = { diff --git a/tools/virsh.c b/tools/virsh.c index f27dd2c..5846304 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -461,15 +461,6 @@ _vshStrdup(vshControl *ctl, const char *s, const char *filename, int line) #define realloc use_vshRealloc_instead_of_realloc #define strdup use_vshStrdup_instead_of_strdup -static int -vshNameSorter(const void *a, const void *b) -{ - const char **sa = (const char**)a; - const char **sb = (const char**)b; - - return vshStrcasecmp(*sa, *sb); -} - static double prettyCapacity(unsigned long long val, const char **unit) { -- 1.7.7.3

The implementation is done manually as the generator does not support wrapping lists of C pointers into Python objects. python/libvirt-override-api.xml: Document python/libvirt-override-virConnect.py: Implementation for listAllSecrets. python/libvirt-override.c: Implementation for the wrapper. --- python/libvirt-override-api.xml | 6 ++++ python/libvirt-override-virConnect.py | 12 ++++++++ python/libvirt-override.c | 48 +++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 0 deletions(-) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index b8ab823..4f609ee 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -368,6 +368,12 @@ <arg name='conn' type='virConnectPtr' info='virConnect connection'/> <return type='str *' info='the list of secret IDs or None in case of error'/> </function> + <function name='virConnectListAllSecrets' file='python'> + <info>returns list of all interfaces</info> + <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> + <arg name='flags' type='unsigned int' info='optional flags'/> + <return type='secret *' info='the list of secrets or None in case of error'/> + </function> <function name='virSecretSetValue' file='libvirt' module='libvirt'> <info>Associates a value with a secret.</info> <return type='int' info='0 on success, -1 on failure.'/> diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index caca982..6bec66d 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -266,3 +266,15 @@ retlist.append(virNWFilter(self, _obj=filter_ptr)) return retlist + + def listAllSecrets(self, flags): + """Returns a list of secret objects""" + ret = libvirtmod.virConnectListAllSecrets(self._o, flags) + if ret is None: + raise libvirtError("virConnectListAllSecrets() failed", conn=self) + + retlist = list() + for secret_ptr in ret: + retlist.append(virSecret(self, _obj=secret_ptr)) + + return retlist diff --git a/python/libvirt-override.c b/python/libvirt-override.c index bdf5d12..0098773 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3592,6 +3592,53 @@ libvirt_virConnectListSecrets(PyObject *self ATTRIBUTE_UNUSED, } static PyObject * +libvirt_virConnectListAllSecrets(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *pyobj_conn; + PyObject *py_retval = NULL; + PyObject *tmp = NULL; + virConnectPtr conn; + virSecretPtr *secrets = NULL; + int c_retval = 0; + int i; + unsigned int flags; + + if (!PyArg_ParseTuple(args, (char *)"Oi:virConnectListAllSecrets", + &pyobj_conn, &flags)) + return NULL; + conn = (virConnectPtr) PyvirConnect_Get(pyobj_conn); + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virConnectListAllSecrets(conn, &secrets, flags); + LIBVIRT_END_ALLOW_THREADS; + if (c_retval < 0) + return VIR_PY_NONE; + + if (!(py_retval = PyList_New(c_retval))) + goto cleanup; + + for (i = 0; i < c_retval; i++) { + if (!(tmp = libvirt_virSecretPtrWrap(secrets[i])) || + PyList_SetItem(py_retval, i, tmp) < 0) { + Py_XDECREF(tmp); + Py_DECREF(py_retval); + py_retval = NULL; + goto cleanup; + } + /* python steals the pointer */ + secrets[i] = NULL; + } + +cleanup: + for (i = 0; i < c_retval; i++) + if (secrets[i]) + virSecretFree(secrets[i]); + VIR_FREE(secrets); + return py_retval; +} + +static PyObject * libvirt_virSecretGetValue(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { PyObject *py_retval; @@ -6179,6 +6226,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virSecretGetUUIDString", libvirt_virSecretGetUUIDString, METH_VARARGS, NULL}, {(char *) "virSecretLookupByUUID", libvirt_virSecretLookupByUUID, METH_VARARGS, NULL}, {(char *) "virConnectListSecrets", libvirt_virConnectListSecrets, METH_VARARGS, NULL}, + {(char *) "virConnectListAllSecrets", libvirt_virConnectListAllSecrets, METH_VARARGS, NULL}, {(char *) "virSecretGetValue", libvirt_virSecretGetValue, METH_VARARGS, NULL}, {(char *) "virSecretSetValue", libvirt_virSecretSetValue, METH_VARARGS, NULL}, {(char *) "virNWFilterGetUUID", libvirt_virNWFilterGetUUID, METH_VARARGS, NULL}, -- 1.7.7.3

Now that we're after the release it would be time to get this series finished up. Could you please post a rebased version (There will be some problem probably after splitting virsh) so that we can get this wrapped up early in the devel phase. Also, I'd like to ask you to send it split by new API function ... so that reviewing is easier. (It's (psychologically) hard to start reviewing a 45 patch series opposed to a 10 patch one :) ). Thanks. Peter

On 2012年09月04日 23:04, Peter Krempa wrote:
Now that we're after the release it would be time to get this series finished up. Could you please post a rebased version (There will be some problem probably after splitting virsh) so that we can get this wrapped up early in the devel phase.
Actually I'm just doing it...
Also, I'd like to ask you to send it split by new API function ... so that reviewing is easier. (It's (psychologically) hard to start reviewing a 45 patch series opposed to a 10 patch one :) ).
... Just finish the pool patches. I will post. Regards, Osier
participants (3)
-
Eric Blake
-
Osier Yang
-
Peter Krempa