[libvirt] [PATCH] esx_storage_driver: Add API to upload volumes (virStorageVolUpload for ESX storage driver)

Patch to add API to upload Volume contents using ESX driver. As stream driver is not supported for ESX, I have used libcurl to transfer the volume, second, using flags here to pass file descriptor of the source file. diff --git a/src/esx/esx_storage_driver.c b/src/esx/esx_storage_driver.c index 9b64891..0519efc 100644 --- a/src/esx/esx_storage_driver.c +++ b/src/esx/esx_storage_driver.c @@ -1643,6 +1643,94 @@ esxStoragePoolIsPersistent(virStoragePoolPtr pool ATTRIBUTE_UNUSED) return 1; } +/** + * esxStorageVolumeUpload + * + * Upload file contents to a given volume. + * Method uses libcurl to POST the contents to ESX server. + * flags is used to pass file descriptor of the + * source file to be read. + */ +static int +esxStorageVolumeUpload(virStorageVolPtr volume, + virStreamPtr stream, + unsigned long long offset, + unsigned long long length, + unsigned int flags) +{ + esxPrivate *priv = volume->conn->privateData; + int err = -1; + char *escapedDatastoreName = NULL; + char *url = NULL; + virBuffer buffer = VIR_BUFFER_INITIALIZER; + int fd = (int)flags; + + /** + * Use CURL module to transfer the file to ESX. + * Use flags to pass the file handle. + */ + const char *unescapedDatastoreName = virStorageVolGetPoolName(volume); + const char *volumeName = virStorageVolGetName(volume); + + if (!unescapedDatastoreName) { + goto cleanup; + } + + if (volume->conn != stream->conn) { + virReportInvalidArg(conn, + _("conn in %s must match stream connection."), + __FUNCTION__); + goto cleanup; + } + + virBufferAsprintf(&buffer, "%s://%s:%d/folder/", + priv->parsedUri->transport, + volume->conn->uri->server, volume->conn->uri->port); + + escapedDatastoreName = esxUtil_EscapeDatastoreItem(unescapedDatastoreName); + + if (escapedDatastoreName == NULL) { + goto cleanup; + } + + /* Prepare URL to upload file */ + virBufferAdd(&buffer, volumeName, strlen(volumeName)); + virBufferAddLit(&buffer, "?dcPath="); + virBufferURIEncodeString(&buffer, priv->primary->datacenterPath); + virBufferAddLit(&buffer, "&dsName="); + virBufferURIEncodeString(&buffer, escapedDatastoreName); + + if (virBufferError(&buffer)) { + virReportOOMError(); + goto cleanup; + } + + url = virBufferContentAndReset(&buffer); + + if (esxVI_EnsureSession(priv->primary) < 0) { + return -1; + } + + /* Upload file */ + if (esxVI_CURL_UploadFile(priv->primary->curl, url, + fd, offset, length) < 0) { + goto cleanup; + } + + err = 0; + + cleanup: + + if (url == NULL) { + virBufferFreeAndReset(&buffer); + } + + VIR_FREE(escapedDatastoreName); + VIR_FREE(url); + + return err; + +} static virStorageDriver esxStorageDriver = { @@ -1673,6 +1761,7 @@ static virStorageDriver esxStorageDriver = { .volGetInfo = esxStorageVolumeGetInfo, /* 0.8.4 */ .volGetXMLDesc = esxStorageVolumeGetXMLDesc, /* 0.8.4 */ .volGetPath = esxStorageVolumeGetPath, /* 0.8.4 */ + .volUpload = esxStorageVolumeUpload, /* 0.9.x */ .poolIsActive = esxStoragePoolIsActive, /* 0.8.2 */ .poolIsPersistent = esxStoragePoolIsPersistent, /* 0.8.2 */ }; diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c index 5b5ab69..831ae36 100644 --- a/src/esx/esx_vi.c +++ b/src/esx/esx_vi.c @@ -425,7 +425,67 @@ esxVI_CURL_Upload(esxVI_CURL *curl, const char *url, const char *content) return 0; } +static size_t +esxVI_CURL_read_callback(void *ptr, size_t size, size_t nmemb, void *stream) +{ + size_t retcode = read((int) stream, ptr, size*nmemb); + + VIR_DEBUG("Read bytes: %d", retcode); + + return retcode; +} + +int +esxVI_CURL_UploadFile(esxVI_CURL *curl, + const char *url, + int fd, + unsigned long long offset, + unsigned long long length) +{ + int error = -1; + int responseCode = 0; + + if (fd < 0) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, "Invalid file descriptor: %d", + fd); + goto cleanup; + } + + if (lseek(fd, (off_t) offset, SEEK_SET) < 0) { + virReportSystemError(errno, "%s", + _("Cannot seek file descriptor ")); + goto cleanup; + } + + virMutexLock(&curl->lock); + + /* set CURL headers */ + curl_easy_setopt(curl->handle, CURLOPT_UPLOAD, 1L); + curl_easy_setopt(curl->handle, CURLOPT_URL, url); + curl_easy_setopt(curl->handle, CURLOPT_READDATA, fd); + curl_easy_setopt(curl->handle, CURLOPT_INFILESIZE_LARGE, + (curl_off_t) length); + curl_easy_setopt(curl->handle, CURLOPT_READFUNCTION, + esxVI_CURL_read_callback); + responseCode = esxVI_CURL_Perform(curl, url); + + virMutexUnlock(&curl->lock); + + if (responseCode < 0) { + goto cleanup; + } else if (responseCode != 200) { + ESX_VI_ERROR(VIR_ERR_INTERNAL_ERROR, + _("HTTP response code %d for upload to '%s'"), + responseCode, url); + goto cleanup; + } + error = 0; + + cleanup: + + return error; +} /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * SharedCURL diff --git a/src/esx/esx_vi.h b/src/esx/esx_vi.h index 78d3986..f95ad1d 100644 --- a/src/esx/esx_vi.h +++ b/src/esx/esx_vi.h @@ -92,7 +92,6 @@ typedef struct _esxVI_EnumerationValue esxVI_EnumerationValue; typedef struct _esxVI_List esxVI_List; - enum _esxVI_APIVersion { esxVI_APIVersion_Undefined = 0, esxVI_APIVersion_Unknown, @@ -167,6 +166,9 @@ void esxVI_CURL_Free(esxVI_CURL **curl); int esxVI_CURL_Connect(esxVI_CURL *curl, esxUtil_ParsedUri *parsedUri); int esxVI_CURL_Download(esxVI_CURL *curl, const char *url, char **content); int esxVI_CURL_Upload(esxVI_CURL *curl, const char *url, const char *content); +int esxVI_CURL_UploadFile(esxVI_CURL *curl, const char *url, int fd, + unsigned long long offset, + unsigned long long length); diff --git a/src/libvirt.c b/src/libvirt.c index 0aa50cb..508593b 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -12537,6 +12537,28 @@ error: return NULL; } +/** + * virStorageVolGetPoolName: + * @vol: pointer to storage volume + * + * Fetch the storage volume pool name. + * + * Returns the pool name, or NULL on error + */ +const char* +virStorageVolGetPoolName(virStorageVolPtr vol) +{ + VIR_DEBUG("vol=%p", vol); + + virResetLastError(); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + virDispatchError(NULL); + return NULL; + } + return vol->pool; +} /** * virStorageVolGetName: diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 2913a81..cdfbf15 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -542,6 +542,7 @@ LIBVIRT_0.9.13 { virDomainSnapshotIsCurrent; virDomainSnapshotListAllChildren; virDomainSnapshotRef; + virStorageVolGetPoolName; } LIBVIRT_0.9.11; # .... define new API here using predicted next version number .... Thanks! Ata Attachment: 1. StorageVolUpload.diff

2012/6/30 Ata Bohra <ata.husain@hotmail.com>:
Patch to add API to upload Volume contents using ESX driver. As stream driver is not supported for ESX, I have used libcurl to transfer the volume, second, using flags here to pass file descriptor of the source file.
This might work for your testing but cannot be included into the official libvirt repo because you're not implementing the upload function correctly. You abused the flags to pass a file descriptor instead of correctly implementing a stream driver for ESX. I can see why you did this. A libcurl based stream driver is not that simple. I know that because I already implemented one for the download part. Another problem is that your approach doesn't support the default VMDK style used by ESX. ESX uses separate files for metadata and content: foobar.vmdk - metadata file foobar-flat.vmdk - content file The problem here with libvirt is that it's storage concept assume that a file based volume is exactly represented by one file. So this doesn't work here. As I mentioned I already have a libcurl based stream driver for the download part ready. I didn't complete this work yet (and that's why it is not yet published) because I need to resolve the two-file problem first. So, give me a day or two to finish this and also implement the upload part. Matthias
participants (2)
-
Ata Bohra
-
Matthias Bolte