This patch implements virDomainScreenshot support for ESX versions newer than 4.0. It adds CreateScreenshot_Task (apiVersion 4.0) and DeleteDatastoreFile_Task (v2.5) then uses those to implement esxDomainScreenshot function. The DeleteDatastoreFile_Task is used to remove the screenshot file that is left on the server after invoking CreateScreenshot_Task call. --- src/esx/esx_driver.c | 173 +++++++++++++++++++++++++++++++++++++++++ src/esx/esx_vi_generator.input | 19 ++++- 2 files changed, 190 insertions(+), 2 deletions(-) diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index ff44881..e825aaf 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -22,6 +22,7 @@ */ #include <config.h> +#include <fcntl.h> #include "internal.h" #include "domain_conf.h" @@ -44,6 +45,8 @@ #include "esx_vi.h" #include "esx_vi_methods.h" #include "esx_util.h" +#include "configmake.h" +#include "fdstream.h" #include "virstring.h" #include "viruri.h" @@ -2501,6 +2504,175 @@ esxDomainGetState(virDomainPtr domain, +static char * +esxDomainScreenshot(virDomainPtr domain, + virStreamPtr st ATTRIBUTE_UNUSED, + unsigned int screen ATTRIBUTE_UNUSED, + unsigned int flags) +{ + esxPrivate *priv = domain->conn->privateData; + esxVI_String *propertyNameList = NULL; + esxVI_ObjectContent *virtualMachine = NULL; + esxVI_VirtualMachinePowerState powerState; + esxVI_ManagedObjectReference *task = NULL; + esxVI_TaskInfoState taskInfoState; + char *taskInfoErrorMessage = NULL; + esxVI_TaskInfo *taskInfo = NULL; + virBuffer buffer = VIR_BUFFER_INITIALIZER; + char *screenshotPath = NULL; + char *dataStoreName = NULL; + char *directoryName = NULL; + char *directoryAndFileName = NULL; + char *url = NULL; + char *screenData = NULL; + unsigned long long screenSize = 0; + char *tmp = NULL; + char *tmpdir = NULL; + int tmp_fd = -1; + char *ret = NULL; + bool unlink_tmp = false; + + virCheckFlags(0, NULL); + + if (esxVI_EnsureSession(priv->primary) < 0) { + return NULL; + } + + if (priv->primary->apiVersion < esxVI_APIVersion_40) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("Operation not supported in VI API version %s, " + "minimum version is 4.0"), + priv->primary->service->about->apiVersion); + } + + if (esxVI_String_AppendValueToList(&propertyNameList, + "runtime.powerState") < 0 || + esxVI_LookupVirtualMachineByUuidAndPrepareForTask + (priv->primary, domain->uuid, propertyNameList, &virtualMachine, + priv->parsedUri->autoAnswer) < 0 || + esxVI_GetVirtualMachinePowerState(virtualMachine, &powerState) < 0) { + goto cleanup; + } + + if (powerState != esxVI_VirtualMachinePowerState_PoweredOn) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("Domain is not powered on")); + goto cleanup; + } + + if (esxVI_CreateScreenshot_Task(priv->primary, virtualMachine->obj, + &task) < 0 || + esxVI_WaitForTaskCompletion(priv->primary, task, domain->uuid, + esxVI_Occurrence_RequiredItem, + priv->parsedUri->autoAnswer, &taskInfoState, + &taskInfoErrorMessage) < 0) { + goto cleanup; + } + + if (taskInfoState != esxVI_TaskInfoState_Success) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not create screenshot: %s"), + taskInfoErrorMessage); + goto cleanup; + } + + if (esxVI_LookupTaskInfoByTask(priv->primary, task, &taskInfo) < 0 || + esxVI_String_CastValueFromAnyType(taskInfo->result, &screenshotPath)) { + goto cleanup; + } + + if (esxUtil_ParseDatastorePath(screenshotPath, &dataStoreName, + &directoryName, &directoryAndFileName) < 0) { + goto cleanup; + } + + virBufferAsprintf(&buffer, "%s://%s:%d/folder/", priv->parsedUri->transport, + domain->conn->uri->server, domain->conn->uri->port); + virBufferURIEncodeString(&buffer, directoryAndFileName); + virBufferAddLit(&buffer, "?dcPath="); + virBufferURIEncodeString(&buffer, priv->primary->datacenterPath); + virBufferAddLit(&buffer, "&dsName="); + virBufferURIEncodeString(&buffer, dataStoreName); + + if (virBufferError(&buffer)) { + virReportOOMError(); + goto cleanup; + } + + url = virBufferContentAndReset(&buffer); + + if (esxVI_CURL_Download(priv->primary->curl, url, &screenData, 0, + &screenSize) < 0) { + goto cleanup; + } + + tmpdir = virGetEnvBlockSUID("TMPDIR"); + if (!tmpdir) tmpdir = "/tmp"; + + if (virAsprintf(&tmp, "%s/esx.screendump.XXXXXX", + tmpdir) < 0) { + goto cleanup; + } + + if ((tmp_fd = mkostemp(tmp, O_CLOEXEC)) == -1) { + virReportSystemError(errno, _("mkostemp(\"%s\") failed"), tmp); + goto endjob; + } + unlink_tmp = true; + + if (safewrite(tmp_fd, screenData, screenSize) < 0) { + virReportSystemError(errno, _("unable to write data to '%s'"), tmp); + goto endjob; + } + + if (VIR_CLOSE(tmp_fd) < 0) { + virReportSystemError(errno, _("unable to close %s"), tmp); + goto endjob; + } + + if (virFDStreamOpenFile(st, tmp, 0, 0, O_RDONLY) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("unable to open stream")); + goto endjob; + } + + if (VIR_STRDUP(ret, "image/png") < 0) { + goto endjob; + } + + esxVI_ManagedObjectReference_Free(&task); + esxVI_DeleteDatastoreFile_Task(priv->primary, screenshotPath, + priv->primary->datacenter->_reference, &task); + + endjob: + VIR_FORCE_CLOSE(tmp_fd); + + if (unlink_tmp) + unlink(tmp); + + VIR_FREE(tmp); + + cleanup: + if (!url) { + virBufferFreeAndReset(&buffer); + } + + esxVI_String_Free(&propertyNameList); + esxVI_ObjectContent_Free(&virtualMachine); + esxVI_ManagedObjectReference_Free(&task); + esxVI_TaskInfo_Free(&taskInfo); + VIR_FREE(taskInfoErrorMessage); + VIR_FREE(screenshotPath); + VIR_FREE(screenData); + VIR_FREE(dataStoreName); + VIR_FREE(directoryName); + VIR_FREE(directoryAndFileName); + VIR_FREE(url); + + return ret; +} + + + static int esxDomainSetVcpusFlags(virDomainPtr domain, unsigned int nvcpus, unsigned int flags) @@ -5221,6 +5393,7 @@ static virDriver esxDriver = { .domainGetMemoryParameters = esxDomainGetMemoryParameters, /* 0.8.6 */ .domainGetInfo = esxDomainGetInfo, /* 0.7.0 */ .domainGetState = esxDomainGetState, /* 0.9.2 */ + .domainScreenshot = esxDomainScreenshot, /* 1.2.3 */ .domainSetVcpus = esxDomainSetVcpus, /* 0.7.0 */ .domainSetVcpusFlags = esxDomainSetVcpusFlags, /* 0.8.5 */ .domainGetVcpusFlags = esxDomainGetVcpusFlags, /* 0.8.5 */ diff --git a/src/esx/esx_vi_generator.input b/src/esx/esx_vi_generator.input index 22c114e..ebc718a 100644 --- a/src/esx/esx_vi_generator.input +++ b/src/esx/esx_vi_generator.input @@ -1,5 +1,5 @@ # -# Definitions of vSphere API 2.5 enumeration and objects types used as input +# Definitions of vSphere API enumeration and objects types used as input # for the esx_vi_generator.py script. # # This format is line-based, so end-of-line is important. @@ -33,11 +33,13 @@ # # Method definition: # -# method <name> [returns <type> <occurrence>] +# method <name> [returns <type> <occurrence>] [apiVersion <version>] # <type> <name> <occurrence> # ... # end # +# If apiVersion is not specified, it defaults to 2.5 +# # The _this parameter can have a type attached to it: # # _this:<member> @@ -1317,6 +1319,11 @@ method CreateFilter returns ManagedObjectReference r end +method CreateScreenshot_Task returns ManagedObjectReference r apiVersion 4.0 + ManagedObjectReference _this r +end + + method CreateSnapshot_Task returns ManagedObjectReference r ManagedObjectReference _this r String name r @@ -1376,6 +1383,14 @@ method Logout end +method DeleteDatastoreFile_Task returns ManagedObjectReference r + ManagedObjectReference _this:fileManager r + String name r + ManagedObjectReference datacenter o +end + + + method MakeDirectory ManagedObjectReference _this:fileManager r String name r -- 1.9.0 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list