diff --git a/src/libvirt.c b/src/libvirt.c index 143d319a44..992e4f2ae0 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -13960,6 +13960,14 @@ virStorageVolDownload(virStorageVolPtr vol, * detect any errors. The results will be unpredictable if * another active stream is writing to the storage volume. * + * When the data stream is closed whether the upload is successful + * or not the target storage pool will be refreshed to reflect pool + * and volume changes as a result of the upload. Depending on + * the target volume storage backend and the source stream type + * for a successful upload, the target volume may take on the + * characteristics from the source stream such as format type, + * capacity, and allocation. + * * Returns 0, or -1 upon error. */ int diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e792a44acb..08111d4bfb 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -825,6 +825,7 @@ virFDStreamCreateFile; virFDStreamOpen; virFDStreamOpenFile; virFDStreamOpenPTY; +virFDStreamSetInternalCloseCb; # libvirt_internal.h diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index efbe5ffab5..b023281890 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -59,6 +59,12 @@ static virStorageDriverStatePtr driverState; static int storageStateCleanup(void); +typedef struct _virStorageVolStreamInfo virStorageVolStreamInfo; +typedef virStorageVolStreamInfo *virStorageVolStreamInfoPtr; +struct _virStorageVolStreamInfo { + char *pool_name; +}; + static void storageDriverLock(virStorageDriverStatePtr driver) { virMutexLock(&driver->lock); @@ -1956,6 +1962,78 @@ storageVolDownload(virStorageVolPtr obj, } +/** + * Frees opaque data. + * + * @opaque Data to be freed. + */ +static void +virStorageVolPoolRefreshDataFree(void *opaque) +{ + virStorageVolStreamInfoPtr cbdata = opaque; + + VIR_FREE(cbdata->pool_name); + VIR_FREE(cbdata); +} + +/** + * Thread to handle the pool refresh + * + * @st Pointer to stream being closed. + * @opaque Domain's device information structure. + */ +static void +virStorageVolPoolRefreshThread(void *opaque) +{ + + virStorageVolStreamInfoPtr cbdata = opaque; + virStoragePoolObjPtr pool = NULL; + virStorageBackendPtr backend; + + storageDriverLock(driverState); + if (!(pool = virStoragePoolObjFindByName(&driverState->pools, + cbdata->pool_name))) + goto cleanup; + + if (!(backend = virStorageBackendForType(pool->def->type))) + goto cleanup; + + virStoragePoolObjClearVols(pool); + if (backend->refreshPool(NULL, pool) < 0) + VIR_DEBUG("Failed to refresh storage pool"); + + cleanup: + if (pool) + virStoragePoolObjUnlock(pool); + storageDriverUnlock(driverState); + virStorageVolPoolRefreshDataFree(cbdata); +} + +/** + * Callback being called if a FDstream is closed. Will spin off a thread + * to perform a pool refresh. + * + * @st Pointer to stream being closed. + * @opaque Buffer to hold the pool name to be rereshed + */ +static void +virStorageVolFDStreamCloseCb(virStreamPtr st ATTRIBUTE_UNUSED, + void *opaque) +{ + virThread thread; + + if (virThreadCreate(&thread, false, virStorageVolPoolRefreshThread, + opaque) < 0) { + /* Not much else can be done */ + VIR_ERROR(_("Failed to create thread to handle pool refresh")); + goto error; + } + return; /* Thread will free opaque data */ + + error: + virStorageVolPoolRefreshDataFree(opaque); +} + static int storageVolUpload(virStorageVolPtr obj, virStreamPtr stream, @@ -1966,6 +2044,7 @@ storageVolUpload(virStorageVolPtr obj, virStorageBackendPtr backend; virStoragePoolObjPtr pool = NULL; virStorageVolDefPtr vol = NULL; + virStorageVolStreamInfoPtr cbdata = NULL; int ret = -1; virCheckFlags(0, -1); @@ -1996,11 +2075,35 @@ storageVolUpload(virStorageVolPtr obj, goto cleanup; } + /* If we have a refreshPool, use the callback routine in order to + * refresh the pool after the volume upload stream closes. This way + * we make sure the volume and pool data are refreshed without user + * interaction and we can just lookup the backend in the callback + * routine in order to call the refresh API. + */ + if (backend->refreshPool) { + if (VIR_ALLOC(cbdata) < 0 || + VIR_STRDUP(cbdata->pool_name, pool->def->name) < 0) + goto cleanup; + } + ret = backend->uploadVol(obj->conn, pool, vol, stream, offset, length, flags); + /* Add cleanup callback - call after uploadVol since the stream + * is then fully set up + */ + if (cbdata) { + virFDStreamSetInternalCloseCb(stream, + virStorageVolFDStreamCloseCb, + cbdata, NULL); + cbdata = NULL; + } + cleanup: virStoragePoolObjUnlock(pool); + if (cbdata) + virStorageVolPoolRefreshDataFree(cbdata); return ret; } diff --git a/tools/virsh.pod b/tools/virsh.pod index 849ae319ae..e377df9156 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -2981,6 +2981,9 @@ of the amount of data to be uploaded. A negative value is interpreted as an unsigned long long value to essentially include everything from the offset to the end of the volume. An error will occur if the I is greater than the specified length. +See the description for the libvirt virStorageVolUpload API for details +regarding possible target volume and pool changes as a result of the +pool refresh when the upload is attempted. =item B [I<--pool> I] [I<--offset> I] [I<--length> I] I I