Hi - The attached patch implements virConnectDiscoverStoragePools mostly as specified by Daniel Berrange in: http://www.redhat.com/archives/libvir-list/2008-February/msg00107.html Daniel wasn't happy with the interface for this function because it wasn't sufficiently general, so it currently doesn't exist. I've attempted to generalize his proposed interface by changing the "hostname" argument to a "srcSpec" argument, which is intended to be a XML storage pool <source> element. Note that srcSpec is not necessary for some (local) pool types. For example, to discover existing LVM volume groups, there's no need to specify a source: virsh # pool-discover logical While discovering nfs shares requires a <source> document to specify the host to query: virsh # pool-discover netfs "<source><host name='share' /></source>" Currently (i.e., in this patch) I've implemented discovery support only for the "logical" and "netfs" pools. I plan on covering the other types once we've agreed upon the API. While this patch is rather large, the vast majority of it is "generic" (not specific to any particular type of storage pool) code, which I mostly took from Daniel's submission. I could easily break the storage-pool-type-specific pieces into separate patches, but those changes are already segregated into the appropriate storage_backend_ files. * Known Issues * I made the virsh interface take a XML string rather than a filename. I found it convenient, but AFAIK the rest of the commands that take XML take it from a file, so perhaps that should be changed (just let me know what you'd prefer). I realize the description of srcSpec is kind of fuzzy. For instance, for netfs discovery, the <source> must have a <host> element (to specify the host to query), but shouldn't have a <dir> element since the exported dirs are exactly what we're trying to discover in this case. So this really needs to be documented accurately for each storage pool type. (Where?) Finally, there's an underspecification issue. The XML pool descriptions returned are supposed to be usable as valid input to virStoragePoolDefineXML. But these descriptions include some data (pool name, target path) that isn't specified by the discover input or the discovered resources. For now, I'm making up a somewhat arbitrary pool name (logical: VolGroup name, netfs: last component of export path). And I don't even specify <target><path> in the "netfs" discovery output (which means it's not valid input to virStoragePoolDefineXML until a target path is added). Dave
diff --git a/configure.in b/configure.in index 8e04f14..5ef0384 100644 --- a/configure.in +++ b/configure.in @@ -660,6 +660,10 @@ if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then fi fi AM_CONDITIONAL([WITH_STORAGE_FS], [test "$with_storage_fs" = "yes"]) +if test "$with_storage_fs" = "yes"; then + AC_PATH_PROG([SHOWMOUNT], [showmount], [], [$PATH:/sbin:/usr/sbin]) + AC_DEFINE_UNQUOTED([SHOWMOUNT], ["$SHOWMOUNT"], [Location or name of the showmount program]) +fi AC_PATH_PROG([QEMU_IMG], [qemu-img], [], [$PATH:/sbin:/usr/sbin:/bin:/usr/bin]) if test -n "$QEMU_IMG" ; then diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 8980bc2..463cf2b 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -890,6 +890,15 @@ int virConnectListDefinedStoragePools(virConnectPtr conn, int maxnames); /* + * Query a host for storage pools of a particular type + */ +int virConnectDiscoverStoragePools(virConnectPtr conn, + const char *type, + const char *srcSpec, + unsigned int flags, + char ***xmlDesc); + +/* * Lookup pool by name or uuid */ virStoragePoolPtr virStoragePoolLookupByName (virConnectPtr conn, @@ -979,6 +988,13 @@ char * virStorageVolGetXMLDesc (virStorageVolPtr pool, char * virStorageVolGetPath (virStorageVolPtr vol); +typedef struct _virStringList virStringList; + +struct _virStringList { + char *val; + struct _virStringList *next; +}; + #ifdef __cplusplus } #endif diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index bd5195c..dc4f049 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -890,6 +890,15 @@ int virConnectListDefinedStoragePools(virConnectPtr conn, int maxnames); /* + * Query a host for storage pools of a particular type + */ +int virConnectDiscoverStoragePools(virConnectPtr conn, + const char *type, + const char *srcSpec, + unsigned int flags, + char ***xmlDesc); + +/* * Lookup pool by name or uuid */ virStoragePoolPtr virStoragePoolLookupByName (virConnectPtr conn, @@ -979,6 +988,13 @@ char * virStorageVolGetXMLDesc (virStorageVolPtr pool, char * virStorageVolGetPath (virStorageVolPtr vol); +typedef struct _virStringList virStringList; + +struct _virStringList { + char *val; + struct _virStringList *next; +}; + #ifdef __cplusplus } #endif diff --git a/python/generator.py b/python/generator.py index 1514c02..66cb3cc 100755 --- a/python/generator.py +++ b/python/generator.py @@ -313,6 +313,7 @@ skip_impl = ( 'virStorageVolGetInfo', 'virStoragePoolGetAutostart', 'virStoragePoolListVolumes', + 'virConnectDiscoverStoragePools' ) diff --git a/qemud/remote.c b/qemud/remote.c index b5a6ec9..f2814e9 100644 --- a/qemud/remote.c +++ b/qemud/remote.c @@ -2958,6 +2958,41 @@ remoteDispatchListStoragePools (struct qemud_server *server ATTRIBUTE_UNUSED, } static int +remoteDispatchDiscoverStoragePools (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req, + remote_discover_storage_pools_args *args, + remote_discover_storage_pools_ret *ret) +{ + CHECK_CONN(client); + char **xmlDesc = NULL; + + /* Allocate return buffer. */ + ret->xml.xml_len = + virConnectDiscoverStoragePools (client->conn, + args->type, + args->srcSpec ? *args->srcSpec : NULL, + args->flags, + &xmlDesc); + if (ret->xml.xml_len == -1) return -1; + + if (ret->xml.xml_len > REMOTE_STORAGE_POOL_NAME_LIST_MAX) { + int i; + for (i = 0 ; i < ret->xml.xml_len ; i++) + free(xmlDesc[i]); + free(xmlDesc); + remoteDispatchError (client, req, + "xmllen > REMOTE_STORAGE_POOL_NAME_LIST_MAX"); + return -2; + } + + ret->xml.xml_val = xmlDesc; + + return 0; +} + + +static int remoteDispatchStoragePoolCreate (struct qemud_server *server ATTRIBUTE_UNUSED, struct qemud_client *client, remote_message_header *req, diff --git a/qemud/remote_dispatch_localvars.h b/qemud/remote_dispatch_localvars.h index d889c8a..6915d5a 100644 --- a/qemud/remote_dispatch_localvars.h +++ b/qemud/remote_dispatch_localvars.h @@ -137,6 +137,8 @@ remote_domain_attach_device_args lv_remote_domain_attach_device_args; remote_num_of_networks_ret lv_remote_num_of_networks_ret; remote_storage_pool_get_info_args lv_remote_storage_pool_get_info_args; remote_storage_pool_get_info_ret lv_remote_storage_pool_get_info_ret; +remote_discover_storage_pools_args lv_remote_discover_storage_pools_args; +remote_discover_storage_pools_ret lv_remote_discover_storage_pools_ret; remote_list_storage_pools_args lv_remote_list_storage_pools_args; remote_list_storage_pools_ret lv_remote_list_storage_pools_ret; remote_domain_restore_args lv_remote_domain_restore_args; diff --git a/qemud/remote_dispatch_proc_switch.h b/qemud/remote_dispatch_proc_switch.h index ebb2433..1850b92 100644 --- a/qemud/remote_dispatch_proc_switch.h +++ b/qemud/remote_dispatch_proc_switch.h @@ -41,6 +41,15 @@ case REMOTE_PROC_AUTH_SASL_STEP: case REMOTE_PROC_CLOSE: fn = (dispatch_fn) remoteDispatchClose; break; +case REMOTE_PROC_DISCOVER_STORAGE_POOLS: + fn = (dispatch_fn) remoteDispatchDiscoverStoragePools; + args_filter = (xdrproc_t) xdr_remote_discover_storage_pools_args; + args = (char *) &lv_remote_discover_storage_pools_args; + memset (&lv_remote_discover_storage_pools_args, 0, sizeof lv_remote_discover_storage_pools_args); + ret_filter = (xdrproc_t) xdr_remote_discover_storage_pools_ret; + ret = (char *) &lv_remote_discover_storage_pools_ret; + memset (&lv_remote_discover_storage_pools_ret, 0, sizeof lv_remote_discover_storage_pools_ret); + break; case REMOTE_PROC_DOMAIN_ATTACH_DEVICE: fn = (dispatch_fn) remoteDispatchDomainAttachDevice; args_filter = (xdrproc_t) xdr_remote_domain_attach_device_args; diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h index 1d9d794..134ec43 100644 --- a/qemud/remote_dispatch_prototypes.h +++ b/qemud/remote_dispatch_prototypes.h @@ -8,6 +8,7 @@ static int remoteDispatchAuthSaslInit (struct qemud_server *server, struct qemud static int remoteDispatchAuthSaslStart (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_auth_sasl_start_args *args, remote_auth_sasl_start_ret *ret); static int remoteDispatchAuthSaslStep (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_auth_sasl_step_args *args, remote_auth_sasl_step_ret *ret); static int remoteDispatchClose (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, void *ret); +static int remoteDispatchDiscoverStoragePools (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_discover_storage_pools_args *args, remote_discover_storage_pools_ret *ret); static int remoteDispatchDomainAttachDevice (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_attach_device_args *args, void *ret); static int remoteDispatchDomainBlockPeek (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_block_peek_args *args, remote_domain_block_peek_ret *ret); static int remoteDispatchDomainBlockStats (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_block_stats_args *args, remote_domain_block_stats_ret *ret); diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c index 1a8e68d..906e2eb 100644 --- a/qemud/remote_protocol.c +++ b/qemud/remote_protocol.c @@ -1501,6 +1501,30 @@ xdr_remote_list_defined_storage_pools_ret (XDR *xdrs, remote_list_defined_storag } bool_t +xdr_remote_discover_storage_pools_args (XDR *xdrs, remote_discover_storage_pools_args *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->type)) + return FALSE; + if (!xdr_remote_string (xdrs, &objp->srcSpec)) + return FALSE; + if (!xdr_u_int (xdrs, &objp->flags)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_discover_storage_pools_ret (XDR *xdrs, remote_discover_storage_pools_ret *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->xml.xml_val; + + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->xml.xml_len, REMOTE_STORAGE_POOL_NAME_LIST_MAX, + sizeof (remote_nonnull_string), (xdrproc_t) xdr_remote_nonnull_string)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_storage_pool_lookup_by_uuid_args (XDR *xdrs, remote_storage_pool_lookup_by_uuid_args *objp) { diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h index 1ad0c54..6021427 100644 --- a/qemud/remote_protocol.h +++ b/qemud/remote_protocol.h @@ -836,6 +836,21 @@ struct remote_list_defined_storage_pools_ret { }; typedef struct remote_list_defined_storage_pools_ret remote_list_defined_storage_pools_ret; +struct remote_discover_storage_pools_args { + remote_nonnull_string type; + remote_string srcSpec; + u_int flags; +}; +typedef struct remote_discover_storage_pools_args remote_discover_storage_pools_args; + +struct remote_discover_storage_pools_ret { + struct { + u_int xml_len; + remote_nonnull_string *xml_val; + } xml; +}; +typedef struct remote_discover_storage_pools_ret remote_discover_storage_pools_ret; + struct remote_storage_pool_lookup_by_uuid_args { remote_uuid uuid; }; @@ -1336,6 +1351,8 @@ extern bool_t xdr_remote_list_storage_pools_ret (XDR *, remote_list_storage_poo extern bool_t xdr_remote_num_of_defined_storage_pools_ret (XDR *, remote_num_of_defined_storage_pools_ret*); extern bool_t xdr_remote_list_defined_storage_pools_args (XDR *, remote_list_defined_storage_pools_args*); extern bool_t xdr_remote_list_defined_storage_pools_ret (XDR *, remote_list_defined_storage_pools_ret*); +extern bool_t xdr_remote_discover_storage_pools_args (XDR *, remote_discover_storage_pools_args*); +extern bool_t xdr_remote_discover_storage_pools_ret (XDR *, remote_discover_storage_pools_ret*); extern bool_t xdr_remote_storage_pool_lookup_by_uuid_args (XDR *, remote_storage_pool_lookup_by_uuid_args*); extern bool_t xdr_remote_storage_pool_lookup_by_uuid_ret (XDR *, remote_storage_pool_lookup_by_uuid_ret*); extern bool_t xdr_remote_storage_pool_lookup_by_name_args (XDR *, remote_storage_pool_lookup_by_name_args*); @@ -1515,6 +1532,8 @@ extern bool_t xdr_remote_list_storage_pools_ret (); extern bool_t xdr_remote_num_of_defined_storage_pools_ret (); extern bool_t xdr_remote_list_defined_storage_pools_args (); extern bool_t xdr_remote_list_defined_storage_pools_ret (); +extern bool_t xdr_remote_discover_storage_pools_args (); +extern bool_t xdr_remote_discover_storage_pools_ret (); extern bool_t xdr_remote_storage_pool_lookup_by_uuid_args (); extern bool_t xdr_remote_storage_pool_lookup_by_uuid_ret (); extern bool_t xdr_remote_storage_pool_lookup_by_name_args (); diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x index 340203c..57f3a5f 100644 --- a/qemud/remote_protocol.x +++ b/qemud/remote_protocol.x @@ -762,6 +762,16 @@ struct remote_list_defined_storage_pools_ret { remote_nonnull_string names<REMOTE_STORAGE_POOL_NAME_LIST_MAX>; }; +struct remote_discover_storage_pools_args { + remote_nonnull_string type; + remote_string srcSpec; + unsigned flags; +}; + +struct remote_discover_storage_pools_ret { + remote_nonnull_string xml<REMOTE_STORAGE_POOL_NAME_LIST_MAX>; +}; + struct remote_storage_pool_lookup_by_uuid_args { remote_uuid uuid; }; diff --git a/src/driver.h b/src/driver.h index e060a38..6388c5e 100644 --- a/src/driver.h +++ b/src/driver.h @@ -448,6 +448,12 @@ typedef int (*virDrvConnectListDefinedStoragePools) (virConnectPtr conn, char **const names, int maxnames); +typedef int + (*virDrvConnectDiscoverStoragePools) (virConnectPtr conn, + const char *type, + const char *srcSpec, + unsigned int flags, + char ***xmlDesc); typedef virStoragePoolPtr (*virDrvStoragePoolLookupByName) (virConnectPtr conn, const char *name); @@ -552,6 +558,7 @@ struct _virStorageDriver { virDrvConnectListStoragePools listPools; virDrvConnectNumOfDefinedStoragePools numOfDefinedPools; virDrvConnectListDefinedStoragePools listDefinedPools; + virDrvConnectDiscoverStoragePools discoverPools; virDrvStoragePoolLookupByName poolLookupByName; virDrvStoragePoolLookupByUUID poolLookupByUUID; virDrvStoragePoolLookupByVolume poolLookupByVolume; diff --git a/src/libvirt.c b/src/libvirt.c index 434ab69..4a00d6e 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -4102,6 +4102,53 @@ virConnectListDefinedStoragePools(virConnectPtr conn, /** + * virConnectDiscoverStoragePools: + * @conn: pointer to hypervisor connection + * @type: type of storage pool(s) to discover + * @srcSpec: XML document specifying discovery source + * @flags: flags for discovery (unused, pass 0) + * @xmlDesc: return array of of XML documents, one per pool + * + * Talks to a storage backend and attempts to auto-discover a + * set of storage pools available. eg For iSCSI this would be a + * set of iSCSI targets. For NFS this would be a list of + * exported paths. The srcSpec (optional for some storage + * pool types, e.g. local ones) is an instance of the storage + * pool's source element specifying where to look for the pools. + * + * Each discovered pool is returned as an XML document + * suitable for feeding into virStoragePoolCreateXML + * + * srcSpec is not required for some types (e.g., those querying + * local storage resources only) + * + * Returns number of discovered pools, or -1 on error + */ +int +virConnectDiscoverStoragePools(virConnectPtr conn, + const char *type, + const char *srcSpec, + unsigned int flags, + char ***xmlDesc) +{ + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + if (type == NULL || xmlDesc == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + return (-1); + } + + if (conn->storageDriver && conn->storageDriver->discoverPools) + return conn->storageDriver->discoverPools (conn, type, srcSpec, flags, xmlDesc); + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + + +/** * virStoragePoolLookupByName: * @conn: pointer to hypervisor connection * @name: name of pool to fetch diff --git a/src/remote_internal.c b/src/remote_internal.c index e88c404..17efd76 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -3044,6 +3044,47 @@ remoteListDefinedStoragePools (virConnectPtr conn, return ret.names.names_len; } +static int +remoteDiscoverStoragePools (virConnectPtr conn, + const char *type, + const char *srcSpec, + unsigned int flags, + char ***xmlDesc) +{ + remote_discover_storage_pools_args args; + remote_discover_storage_pools_ret ret; + GET_STORAGE_PRIVATE (conn, -1); + const char *emptyString = ""; + + args.type = (char*)type; + /* + * I'd think the following would work here: + * args.srcSpec = (char**)&srcSpec; + * since srcSpec is a remote_string (not a remote_nonnull_string). + * + * But when srcSpec is NULL, this yields: + * libvir: Remote error : marshalling args + * + * So for now I'm working around this by turning NULL srcSpecs + * into empty strings. + */ + args.srcSpec = srcSpec ? (char **)&srcSpec : (char **)&emptyString; + args.flags = flags; + + memset (&ret, 0, sizeof ret); + if (call (conn, priv, 0, REMOTE_PROC_DISCOVER_STORAGE_POOLS, + (xdrproc_t) xdr_remote_discover_storage_pools_args, (char *) &args, + (xdrproc_t) xdr_remote_discover_storage_pools_ret, (char *) &ret) == -1) + return -1; + + *xmlDesc = ret.xml.xml_val; + ret.xml.xml_val = NULL; /* To stop xdr_free free'ing it */ + + xdr_free ((xdrproc_t) xdr_remote_discover_storage_pools_ret, (char *) &ret); + + return ret.xml.xml_len; +} + static virStoragePoolPtr remoteStoragePoolLookupByUUID (virConnectPtr conn, const unsigned char *uuid) @@ -4904,6 +4945,7 @@ static virStorageDriver storage_driver = { .listPools = remoteListStoragePools, .numOfDefinedPools = remoteNumOfDefinedStoragePools, .listDefinedPools = remoteListDefinedStoragePools, + .discoverPools = remoteDiscoverStoragePools, .poolLookupByUUID = remoteStoragePoolLookupByUUID, .poolLookupByName = remoteStoragePoolLookupByName, .poolLookupByVolume = remoteStoragePoolLookupByVolume, diff --git a/src/storage_backend.c b/src/storage_backend.c index a164a08..b9db550 100644 --- a/src/storage_backend.c +++ b/src/storage_backend.c @@ -510,12 +510,13 @@ virStorageBackendRunProgRegex(virConnectPtr conn, * If there are no input tokens (empty input), call FUNC with N_COLUMNS == 0. */ int -virStorageBackendRunProgNul(virConnectPtr conn, - virStoragePoolObjPtr pool, - const char **prog, - size_t n_columns, - virStorageBackendListVolNulFunc func, - void *data) +virStorageBackendRunProgDelim(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + int delim, + size_t n_columns, + virStorageBackendListVolNulFunc func, + void *data) { size_t n_tok = 0; int child = 0, fd = -1, exitstatus; @@ -552,7 +553,7 @@ virStorageBackendRunProgNul(virConnectPtr conn, size_t buf_len = 0; /* Be careful: even when it returns -1, this use of getdelim allocates memory. */ - ssize_t tok_len = getdelim (&buf, &buf_len, 0, fp); + ssize_t tok_len = getdelim (&buf, &buf_len, delim, fp); v[n_tok] = buf; if (tok_len < 0) { /* Maybe EOF, maybe an error. diff --git a/src/storage_backend.h b/src/storage_backend.h index 797ca01..631a2ea 100644 --- a/src/storage_backend.h +++ b/src/storage_backend.h @@ -63,6 +63,7 @@ struct _virStorageBackendPoolOptions { virStoragePoolFormatFromString formatFromString; }; +typedef int (*virStorageBackendDiscoverPools)(virConnectPtr conn, const char *srcSpec, unsigned int flags, char ***xmlDescs); typedef int (*virStorageBackendStartPool)(virConnectPtr conn, virStoragePoolObjPtr pool); typedef int (*virStorageBackendBuildPool)(virConnectPtr conn, virStoragePoolObjPtr pool, unsigned int flags); typedef int (*virStorageBackendRefreshPool)(virConnectPtr conn, virStoragePoolObjPtr pool); @@ -80,6 +81,7 @@ typedef virStorageBackend *virStorageBackendPtr; struct _virStorageBackend { int type; + virStorageBackendDiscoverPools discoverPools; virStorageBackendStartPool startPool; virStorageBackendBuildPool buildPool; virStorageBackendRefreshPool refreshPool; @@ -136,11 +138,15 @@ int virStorageBackendRunProgRegex(virConnectPtr conn, void *data, int *exitstatus); -int virStorageBackendRunProgNul(virConnectPtr conn, - virStoragePoolObjPtr pool, - const char **prog, - size_t n_columns, - virStorageBackendListVolNulFunc func, - void *data); +int virStorageBackendRunProgDelim(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char **prog, + int delim, + size_t n_columns, + virStorageBackendListVolNulFunc func, + void *data); + +#define virStorageBackendRunProgNul(conn, pool, prog, n_columns, func, data) \ + virStorageBackendRunProgDelim((conn), (pool), (prog), (n_columns), (func), (data)) #endif /* __VIR_STORAGE_BACKEND_H__ */ diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c index f195034..75013e8 100644 --- a/src/storage_backend_fs.c +++ b/src/storage_backend_fs.c @@ -35,12 +35,18 @@ #include <byteswap.h> #include <mntent.h> #include <string.h> +#include "c-ctype.h" + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> #include "internal.h" #include "storage_backend_fs.h" #include "storage_conf.h" #include "util.h" #include "memory.h" +#include "xml.h" enum { VIR_STORAGE_POOL_FS_AUTO = 0, @@ -442,6 +448,149 @@ static int virStorageBackendProbeFile(virConnectPtr conn, } #if WITH_STORAGE_FS +struct _virNetfsDiscoverState { + const char *host; + virStringList *list; +}; + +typedef struct _virNetfsDiscoverState virNetfsDiscoverState; + +static int +virStorageBackendFileSystemNetDiscoverPoolsFunc(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + char **const groups, + void *data) +{ + virNetfsDiscoverState *state = data; + virStringList *newItem; + const char *name, *path; + int len; + + path = groups[0]; + + name = rindex(path, '/'); + if (name == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("no / in path?")); + return -1; + } + name += 1; + + /* Append new XML desc to list */ + + if (VIR_ALLOC(newItem) != 0) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("new xml desc")); + return -1; + } + + + /* regexp pattern is too greedy, so we may have trailing spaces to trim. + * NB showmount output ambiguous for (evil) exports with names w/trailing whitespace + */ + len = 0; + while (name[len]) + len++; + while (c_isspace(name[len-1])) + len--; + if (asprintf(&newItem->val, + "<pool type='netfs' ><name>%.*s</name>" + "<source><host name='%s' /><dir path='%.*s' /></source>" + "</pool>", len, name, state->host, len + (int)(name - path), path) <= 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("asprintf failed")); + VIR_FREE(newItem); + return -1; + } + + newItem->next = state->list; + state->list = newItem; + + return 0; +} + +static int +virStorageBackendFileSystemNetDiscoverPools(virConnectPtr conn, + const char *srcSpec, + unsigned int flags ATTRIBUTE_UNUSED, + char ***xmlDescs) +{ + /* + * # showmount --no-headers -e HOSTNAME + * /tmp * + * /A dir demo1.foo.bar,demo2.foo.bar + * + * Extract directory name (including possible interior spaces ...). + */ + + const char *regexes[] = { + "^(/.*) +\\S+$" + }; + int vars[] = { + 1 + }; + xmlDocPtr doc = NULL; + xmlXPathContextPtr xpath_ctxt = NULL; + virNetfsDiscoverState state = { .host = NULL, .list = NULL }; + const char *prog[] = { SHOWMOUNT, "--no-headers", "--exports", NULL, NULL }; + int exitstatus, n_descs = -1; + virStringList *p; + + doc = xmlReadDoc((const xmlChar *)srcSpec, "srcSpec.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING); + if (doc == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "%s", _("bad <source> spec")); + goto cleanup; + } + + xpath_ctxt = xmlXPathNewContext(doc); + if (xpath_ctxt == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("xpath_ctxt")); + goto cleanup; + } + + state.host = virXPathString("string(/source/host/@name)", xpath_ctxt); + if (!state.host || !state.host[0]) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, "%s", + _("missing <host> in <source> spec")); + goto cleanup; + } + prog[3] = state.host; + + if (virStorageBackendRunProgRegex(conn, NULL, prog, 1, regexes, vars, + virStorageBackendFileSystemNetDiscoverPoolsFunc, + &state, &exitstatus) < 0) + goto cleanup; + + /* Count items in list */ + n_descs = 0; + for (p = state.list; p; p = p->next) + n_descs += 1; + + /* Allocate array and populate from list */ + if (VIR_ALLOC_N(*xmlDescs, n_descs) < 0) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("xml desc array")); + n_descs = -1; + goto cleanup; + } + n_descs = 0; + for (p = state.list; p; p = p->next) + (*xmlDescs)[n_descs++] = p->val; + + cleanup: + if (doc) + xmlFreeDoc(doc); + if (xpath_ctxt) + xmlXPathFreeContext(xpath_ctxt); + VIR_FREE(state.host); + while (state.list) { + p = state.list->next; + VIR_FREE(state.list); + state.list = p; + } + + return n_descs; +} + + /** * @conn connection to report errors against * @pool storage pool to check for status @@ -1114,6 +1263,7 @@ virStorageBackend virStorageBackendNetFileSystem = { .buildPool = virStorageBackendFileSystemBuild, .startPool = virStorageBackendFileSystemStart, + .discoverPools = virStorageBackendFileSystemNetDiscoverPools, .refreshPool = virStorageBackendFileSystemRefresh, .stopPool = virStorageBackendFileSystemStop, .deletePool = virStorageBackendFileSystemDelete, diff --git a/src/storage_backend_logical.c b/src/storage_backend_logical.c index 9a0c27f..3c16d20 100644 --- a/src/storage_backend_logical.c +++ b/src/storage_backend_logical.c @@ -30,6 +30,7 @@ #include <string.h> #include <unistd.h> #include <fcntl.h> +#include "c-ctype.h" #include "internal.h" #include "storage_backend_logical.h" @@ -257,6 +258,90 @@ virStorageBackendLogicalRefreshPoolFunc(virConnectPtr conn ATTRIBUTE_UNUSED, static int +virStorageBackendLogicalDiscoverPoolsFunc(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + size_t n_tokens, + char **const tokens, + void *data) +{ + virStringList **rest = data; + virStringList *newItem; + const char *name; + int len; + + /* Append new XML desc to list */ + + if (VIR_ALLOC(newItem) != 0) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("new xml desc")); + return -1; + } + + if (n_tokens != 1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("n_tokens should be 1")); + return -1; + } + + + name = tokens[0]; + virSkipSpaces(&name); + len = 0; + while (name[len]) + len++; + while (c_isspace(name[len-1])) + len--; + if (asprintf(&newItem->val, + "<pool type='logical' ><name>%.*s</name>" + "<target><path>/dev/%.*s</path></target>" + "</pool>", len, name, len, name) <= 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("asprintf failed")); + VIR_FREE(newItem); + return -1; + } + + newItem->next = *rest; + *rest = newItem; + + return 0; +} + +static int +virStorageBackendLogicalDiscoverPools(virConnectPtr conn, + const char *srcSpec ATTRIBUTE_UNUSED, + unsigned int flags ATTRIBUTE_UNUSED, + char ***xmlDescs) +{ + const char *prog[] = { VGS, "--noheadings", "-o", "vg_name", NULL }; + virStringList *descs = NULL; + int n_descs; + virStringList *p, *nextp; + + if (virStorageBackendRunProgDelim(conn, NULL, prog, '\n', 1, + virStorageBackendLogicalDiscoverPoolsFunc, + &descs) < 0) + return -1; + + /* Count items in list */ + n_descs = 0; + for (p = descs; p; p = p->next) + n_descs += 1; + + /* Allocate array and populate from list, freeing list nodes as we go */ + if (VIR_ALLOC_N(*xmlDescs, n_descs) < 0) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("xml desc array")); + return -1; + } + n_descs = 0; + for (p = descs; p; p = nextp) { + nextp = p->next; + (*xmlDescs)[n_descs++] = p->val; + VIR_FREE(p); + } + + return n_descs; +} + + +static int virStorageBackendLogicalStartPool(virConnectPtr conn, virStoragePoolObjPtr pool) { @@ -520,6 +605,7 @@ virStorageBackendLogicalDeleteVol(virConnectPtr conn, virStorageBackend virStorageBackendLogical = { .type = VIR_STORAGE_POOL_LOGICAL, + .discoverPools = virStorageBackendLogicalDiscoverPools, .startPool = virStorageBackendLogicalStartPool, .buildPool = virStorageBackendLogicalBuildPool, .refreshPool = virStorageBackendLogicalRefreshPool, diff --git a/src/storage_driver.c b/src/storage_driver.c index 45f2635..0d25801 100644 --- a/src/storage_driver.c +++ b/src/storage_driver.c @@ -386,6 +386,31 @@ storageListDefinedPools(virConnectPtr conn, return -1; } +static int +storagePoolDiscover(virConnectPtr conn, + const char *type, + const char *srcSpec, + unsigned int flags, + char ***xmlDesc) +{ + int backend_type; + virStorageBackendPtr backend; + + backend_type = virStorageBackendFromString(type); + if (backend_type < 0) + return -1; + + backend = virStorageBackendForType(backend_type); + if (backend == NULL) + return -1; + + if (backend->discoverPools) + return backend->discoverPools(conn, srcSpec, flags, xmlDesc); + + return 0; +} + + static virStoragePoolPtr storagePoolCreate(virConnectPtr conn, const char *xml, @@ -1212,6 +1237,7 @@ static virStorageDriver storageDriver = { storageListPools, storageNumDefinedPools, storageListDefinedPools, + storagePoolDiscover, storagePoolLookupByName, storagePoolLookupByUUID, storagePoolLookupByVolume, diff --git a/src/virsh.c b/src/virsh.c index 620f103..48d443e 100644 --- a/src/virsh.c +++ b/src/virsh.c @@ -3430,6 +3430,59 @@ cmdPoolList(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED) return TRUE; } +/* + * "pool-discover" command + */ +static vshCmdInfo info_pool_discover[] = { + {"syntax", "pool-discover <type> [srcSpec]"}, + {"help", gettext_noop("discover pools")}, + {"desc", gettext_noop("Returns discover of pools.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_pool_discover[] = { + {"type", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("type of storage pool to discover")}, + {"srcSpec", VSH_OT_DATA, VSH_OFLAG_NONE, gettext_noop("optional source to query for pools")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdPoolDiscover(vshControl * ctl, vshCmd * cmd ATTRIBUTE_UNUSED) +{ + char *type, *srcSpec; + char **xmlDesc = NULL; + int npool, i; + int found; + + type = vshCommandOptString(cmd, "type", &found); + if (!found) + return FALSE; + srcSpec = vshCommandOptString(cmd, "srcSpec", &found); + if (!found) + srcSpec = NULL; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if ((npool = virConnectDiscoverStoragePools(ctl->conn, + type, + srcSpec, + 0, + &xmlDesc)) < 0) { + vshError(ctl, FALSE, "%s", _("Failed to discover pools")); + return FALSE; + } + + for (i = 0 ; i < npool ; i++) { + vshPrint(ctl, "%s\n", xmlDesc[i]); + free(xmlDesc[i]); + } + free(xmlDesc); + + return TRUE; +} + + static double prettyCapacity(unsigned long long val, const char **unit) { @@ -5137,6 +5190,7 @@ static vshCmdDef commands[] = { {"pool-define-as", cmdPoolDefineAs, opts_pool_define_as, info_pool_define_as}, {"pool-destroy", cmdPoolDestroy, opts_pool_destroy, info_pool_destroy}, {"pool-delete", cmdPoolDelete, opts_pool_delete, info_pool_delete}, + {"pool-discover", cmdPoolDiscover, opts_pool_discover, info_pool_discover}, {"pool-dumpxml", cmdPoolDumpXML, opts_pool_dumpxml, info_pool_dumpxml}, {"pool-info", cmdPoolInfo, opts_pool_info, info_pool_info}, {"pool-list", cmdPoolList, opts_pool_list, info_pool_list},
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list